Merged PR 6140256: Migrate OSS up to 94d39b758
Related work items: MSFT-33686498
This commit is contained in:
commit
66f0a9cd70
8
.github/ISSUE_TEMPLATE/Bug_Report.yml
vendored
8
.github/ISSUE_TEMPLATE/Bug_Report.yml
vendored
|
@ -1,14 +1,10 @@
|
|||
name: "Bug report 🐛"
|
||||
description: Report errors or unexpected behavior
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
issue_body: true
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please make sure to [search for existing issues](https://github.com/microsoft/terminal/issues) before filing a new one!
|
||||
Please make sure to [search for existing issues](https://github.com/microsoft/terminal/issues) and [check the FAQ](https://github.com/microsoft/terminal/wiki/Frequently-Asked-Questions-(FAQ)) before filing a new one!
|
||||
|
||||
If this is an application crash, please also provide a [Feedback Hub](https://aka.ms/terminal-feedback-hub) submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal" and choose "Share My Feedback" after submission to get the link.
|
||||
|
||||
|
@ -44,7 +40,7 @@ body:
|
|||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: If you want to include screenshots, paste them into the markdown editor below the form or follow up with a separate comment.
|
||||
description: If you want to include screenshots, paste them into the markdown editor below.
|
||||
placeholder: What were you expecting?
|
||||
validations:
|
||||
required: false
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<!-- markdownlint-disable MD033 MD041 -->
|
||||
<details>
|
||||
<summary>
|
||||
:pencil2: Contributor please read this
|
||||
|
@ -8,9 +9,10 @@ By default the command suggestion will generate a file named based on your commi
|
|||
:warning: The command is written for posix shells. You can copy the contents of each `perl` command excluding the outer `'` marks and dropping any `'"`/`"'` quotation mark pairs into a file and then run `perl file.pl` from the root of the repository to run the code. Alternatively, you can manually insert the items...
|
||||
|
||||
If the listed items are:
|
||||
|
||||
* ... **misspelled**, then please *correct* them instead of using the command.
|
||||
* ... *names*, please add them to `.github/actions/spelling/dictionary/names.txt`.
|
||||
* ... APIs, you can add them to a file in `.github/actions/spelling/dictionary/`.
|
||||
* ... *names*, please add them to `.github/actions/spelling/allow/names.txt`.
|
||||
* ... APIs, you can add them to a file in `.github/actions/spelling/allow/`.
|
||||
* ... just things you're using, please add them to an appropriate file in `.github/actions/spelling/expect/`.
|
||||
* ... tokens you only need in one place and shouldn't *generally be used*, you can add an item in an appropriate file in `.github/actions/spelling/patterns/`.
|
||||
|
||||
|
@ -18,10 +20,31 @@ See the `README.md` in each directory for more information.
|
|||
|
||||
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
|
||||
|
||||
:clamp: If you see a bunch of garbage and it relates to a binary-ish string, please add a file path to the `.github/actions/spelling/excludes.txt` file instead of just accepting the garbage.
|
||||
<details><summary>:clamp: If you see a bunch of garbage</summary>
|
||||
|
||||
If it relates to a ...
|
||||
<details><summary>well-formed pattern</summary>
|
||||
|
||||
See if there's a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it.
|
||||
|
||||
If not, try writing one and adding it to a `patterns/{file}.txt`.
|
||||
|
||||
Patterns are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
|
||||
|
||||
Note that patterns can't match multiline strings.
|
||||
</details>
|
||||
<details><summary>binary-ish string</summary>
|
||||
|
||||
Please add a file path to the `excludes.txt` file instead of just accepting the garbage.
|
||||
|
||||
File paths are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
|
||||
|
||||
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](https://github.com/microsoft/terminal/blob/main/README.md) (on whichever branch you're using).
|
||||
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
|
||||
../tree/HEAD/README.md) (on whichever branch you're using).
|
||||
</details>
|
||||
|
||||
</details>
|
||||
|
||||
</details>
|
|
@ -1,4 +1,4 @@
|
|||
# Dictionaries are lists of words to accept unconditionally
|
||||
# Allow files are lists of words to accept unconditionally
|
||||
|
||||
While check spelling will complain about an expected word
|
||||
which is no longer present, you can include things here even if
|
||||
|
@ -8,11 +8,11 @@ E.g., you could include a list of system APIs here, or potential
|
|||
contributors (so that if a future commit includes their name,
|
||||
it'll be accepted).
|
||||
|
||||
### Files
|
||||
## Files
|
||||
|
||||
| File | Description |
|
||||
| ---- | ----------- |
|
||||
| [Dictionary](dictionary.txt) | Primary US English dictionary |
|
||||
| [Allow](allow.txt) | Supplements to the dictionary |
|
||||
| [Chinese](chinese.txt) | Chinese words |
|
||||
| [Japanese](japanese.txt) | Japanese words |
|
||||
| [Microsoft](microsoft.txt) | Microsoft brand items |
|
53
.github/actions/spelling/allow/allow.txt
vendored
Normal file
53
.github/actions/spelling/allow/allow.txt
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
Apc
|
||||
apc
|
||||
clickable
|
||||
copyable
|
||||
dalet
|
||||
Dcs
|
||||
dcs
|
||||
dialytika
|
||||
dje
|
||||
downside
|
||||
downsides
|
||||
dze
|
||||
dzhe
|
||||
Enum'd
|
||||
ftp
|
||||
geeksforgeeks
|
||||
ghe
|
||||
gje
|
||||
hostname
|
||||
hostnames
|
||||
hyperlink
|
||||
hyperlinking
|
||||
hyperlinks
|
||||
img
|
||||
It'd
|
||||
kje
|
||||
lje
|
||||
maxed
|
||||
mru
|
||||
nje
|
||||
ogonek
|
||||
ok'd
|
||||
overlined
|
||||
postmodern
|
||||
ptys
|
||||
qof
|
||||
reimplementation
|
||||
reserialization
|
||||
reserialize
|
||||
reserializes
|
||||
runtimes
|
||||
shcha
|
||||
Sos
|
||||
timestamped
|
||||
tokenizes
|
||||
tonos
|
||||
tshe
|
||||
UIs
|
||||
versioned
|
||||
We'd
|
||||
wildcards
|
||||
yeru
|
||||
zhe
|
|
@ -1,19 +1,25 @@
|
|||
ACCEPTFILES
|
||||
ACCESSDENIED
|
||||
alignas
|
||||
alignof
|
||||
bitfield
|
||||
bitfields
|
||||
BUILDNUMBER
|
||||
charconv
|
||||
CLASSNOTAVAILABLE
|
||||
cmdletbinding
|
||||
COLORPROPERTY
|
||||
colspan
|
||||
COMDLG
|
||||
cstdint
|
||||
CXICON
|
||||
CYICON
|
||||
D2DERR_SHADER_COMPILE_FAILED
|
||||
dataobject
|
||||
dcomp
|
||||
DERR
|
||||
dlldata
|
||||
DONTADDTORECENT
|
||||
DWORDLONG
|
||||
environstrings
|
||||
EXPCMDFLAGS
|
||||
EXPCMDSTATE
|
||||
|
@ -30,6 +36,7 @@ HIGHCONTRASTON
|
|||
HIGHCONTRASTW
|
||||
hotkeys
|
||||
href
|
||||
hrgn
|
||||
IActivation
|
||||
IApp
|
||||
IAppearance
|
||||
|
@ -43,10 +50,12 @@ ICustom
|
|||
IDialog
|
||||
IDirect
|
||||
IExplorer
|
||||
IFACEMETHOD
|
||||
IFile
|
||||
IInheritable
|
||||
IMap
|
||||
IObject
|
||||
iosfwd
|
||||
IPackage
|
||||
IPeasant
|
||||
IStorage
|
||||
|
@ -54,6 +63,7 @@ istream
|
|||
IStringable
|
||||
ITab
|
||||
ITaskbar
|
||||
IUri
|
||||
IVirtual
|
||||
LCID
|
||||
llabs
|
||||
|
@ -61,7 +71,6 @@ llu
|
|||
localtime
|
||||
lround
|
||||
LSHIFT
|
||||
MULTIPLEUSE
|
||||
msappx
|
||||
MULTIPLEUSE
|
||||
NCHITTEST
|
||||
|
@ -72,9 +81,12 @@ NOASYNC
|
|||
NOCHANGEDIR
|
||||
NOPROGRESS
|
||||
NOREDIRECTIONBITMAP
|
||||
NOREPEAT
|
||||
ntprivapi
|
||||
oaidl
|
||||
ocidl
|
||||
osver
|
||||
OSVERSIONINFOEXW
|
||||
otms
|
||||
OUTLINETEXTMETRICW
|
||||
overridable
|
||||
|
@ -82,32 +94,29 @@ PAGESCROLL
|
|||
PICKFOLDERS
|
||||
pmr
|
||||
REGCLS
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
roundf
|
||||
RSHIFT
|
||||
rx
|
||||
schandle
|
||||
semver
|
||||
serializer
|
||||
SHELLEXECUTEINFOW
|
||||
shobjidl
|
||||
SINGLEUSE
|
||||
SHOWMINIMIZED
|
||||
SINGLEUSE
|
||||
SIZENS
|
||||
smoothstep
|
||||
GETDESKWALLPAPER
|
||||
SHELLEXECUTEINFOW
|
||||
snprintf
|
||||
spsc
|
||||
sregex
|
||||
STDCPP
|
||||
strchr
|
||||
STDMETHOD
|
||||
strchr
|
||||
streambuf
|
||||
Stubless
|
||||
Subheader
|
||||
Subpage
|
||||
UPDATEINIFILE
|
||||
syscall
|
||||
TBPF
|
||||
THEMECHANGED
|
||||
|
@ -115,7 +124,6 @@ tmp
|
|||
tolower
|
||||
TTask
|
||||
TVal
|
||||
tx
|
||||
UPDATEINIFILE
|
||||
userenv
|
||||
wcsstr
|
||||
|
@ -126,9 +134,19 @@ wsregex
|
|||
wwinmain
|
||||
XDocument
|
||||
XElement
|
||||
xfacet
|
||||
xhash
|
||||
xiosbase
|
||||
xlocale
|
||||
xlocbuf
|
||||
xlocinfo
|
||||
xlocmes
|
||||
xlocmon
|
||||
xlocnum
|
||||
xloctime
|
||||
xmemory
|
||||
XParse
|
||||
xstddef
|
||||
xstring
|
||||
xtree
|
||||
xutility
|
117
.github/actions/spelling/allow/colors.txt
vendored
Normal file
117
.github/actions/spelling/allow/colors.txt
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
alice
|
||||
aliceblue
|
||||
antiquewhite
|
||||
blanchedalmond
|
||||
blueviolet
|
||||
burlywood
|
||||
cadetblue
|
||||
cornflowerblue
|
||||
cornsilk
|
||||
cyan
|
||||
darkblue
|
||||
darkcyan
|
||||
darkgoldenrod
|
||||
darkgray
|
||||
darkgreen
|
||||
darkgrey
|
||||
darkkhaki
|
||||
darkmagenta
|
||||
darkolivegreen
|
||||
darkorange
|
||||
darkorchid
|
||||
darkred
|
||||
darksalmon
|
||||
darkseagreen
|
||||
darkslateblue
|
||||
darkslategray
|
||||
darkslategrey
|
||||
darkturquoise
|
||||
darkviolet
|
||||
deeppink
|
||||
deepskyblue
|
||||
dimgray
|
||||
dimgrey
|
||||
dodgerblue
|
||||
firebrick
|
||||
floralwhite
|
||||
forestgreen
|
||||
gainsboro
|
||||
ghostwhite
|
||||
greenyellow
|
||||
hotpink
|
||||
indian
|
||||
indianred
|
||||
lavenderblush
|
||||
lawngreen
|
||||
lemonchiffon
|
||||
lightblue
|
||||
lightcoral
|
||||
lightcyan
|
||||
lightgoldenrod
|
||||
lightgoldenrodyellow
|
||||
lightgray
|
||||
lightgreen
|
||||
lightgrey
|
||||
lightpink
|
||||
lightsalmon
|
||||
lightseagreen
|
||||
lightskyblue
|
||||
lightslateblue
|
||||
lightslategray
|
||||
lightslategrey
|
||||
lightsteelblue
|
||||
lightyellow
|
||||
limegreen
|
||||
mediumaquamarine
|
||||
mediumblue
|
||||
mediumorchid
|
||||
mediumpurple
|
||||
mediumseagreen
|
||||
mediumslateblue
|
||||
mediumspringgreen
|
||||
mediumturquoise
|
||||
mediumvioletred
|
||||
midnightblue
|
||||
mintcream
|
||||
mistyrose
|
||||
navajo
|
||||
navajowhite
|
||||
navyblue
|
||||
oldlace
|
||||
olivedrab
|
||||
orangered
|
||||
palegoldenrod
|
||||
palegreen
|
||||
paleturquoise
|
||||
palevioletred
|
||||
papayawhip
|
||||
peachpuff
|
||||
peru
|
||||
powderblue
|
||||
rebecca
|
||||
rebeccapurple
|
||||
rosybrown
|
||||
royalblue
|
||||
saddlebrown
|
||||
sandybrown
|
||||
seagreen
|
||||
sienna
|
||||
skyblue
|
||||
slateblue
|
||||
slategray
|
||||
slategrey
|
||||
springgreen
|
||||
steelblue
|
||||
violetred
|
||||
webgray
|
||||
webgreen
|
||||
webgrey
|
||||
webmaroon
|
||||
webpurple
|
||||
whitesmoke
|
||||
xaroon
|
||||
xray
|
||||
xreen
|
||||
xrey
|
||||
xurple
|
||||
yellowgreen
|
|
@ -1,3 +1,3 @@
|
|||
isnan
|
||||
powf
|
||||
sqrtf
|
||||
isnan
|
|
@ -3,13 +3,18 @@ ADMINS
|
|||
altform
|
||||
altforms
|
||||
appendwttlogging
|
||||
appx
|
||||
appxbundle
|
||||
appxerror
|
||||
appxmanifest
|
||||
ATL
|
||||
backplating
|
||||
bitmaps
|
||||
BOMs
|
||||
CPLs
|
||||
CPRs
|
||||
cpptools
|
||||
cppvsdbg
|
||||
CPRs
|
||||
DACL
|
||||
DACLs
|
||||
diffs
|
||||
|
@ -38,13 +43,17 @@ robocopy
|
|||
SACLs
|
||||
Shobjidl
|
||||
Skype
|
||||
sxs
|
||||
Sysinternals
|
||||
sysnative
|
||||
systemroot
|
||||
taskkill
|
||||
tasklist
|
||||
tdbuildteamid
|
||||
unvirtualized
|
||||
VCRT
|
||||
vcruntime
|
||||
Virtualization
|
||||
visualstudio
|
||||
vscode
|
||||
VSTHRD
|
|
@ -13,6 +13,7 @@ ethanschoonover
|
|||
Firefox
|
||||
Gatta
|
||||
glsl
|
||||
Gravell
|
||||
Grie
|
||||
Griese
|
||||
Hernan
|
||||
|
@ -69,8 +70,8 @@ Wirt
|
|||
Wojciech
|
||||
zadjii
|
||||
Zamor
|
||||
zamora
|
||||
Zamora
|
||||
zamora
|
||||
Zoey
|
||||
zorio
|
||||
Zverovich
|
782
.github/actions/spelling/dictionary/colors.txt
vendored
782
.github/actions/spelling/dictionary/colors.txt
vendored
|
@ -1,782 +0,0 @@
|
|||
snow
|
||||
ghost-white
|
||||
ghostwhite
|
||||
white-smoke
|
||||
whitesmoke
|
||||
gainsboro
|
||||
floral-white
|
||||
floralwhite
|
||||
old-lace
|
||||
oldlace
|
||||
linen
|
||||
antique-white
|
||||
antiquewhite
|
||||
papaya-whip
|
||||
papayawhip
|
||||
blanched-almond
|
||||
blanchedalmond
|
||||
bisque
|
||||
peach-puff
|
||||
peachpuff
|
||||
navajo-white
|
||||
navajowhite
|
||||
moccasin
|
||||
cornsilk
|
||||
ivory
|
||||
lemon-chiffon
|
||||
lemonchiffon
|
||||
seashell
|
||||
honeydew
|
||||
mint-cream
|
||||
mintcream
|
||||
azure
|
||||
alice-blue
|
||||
aliceblue
|
||||
lavender
|
||||
lavender-blush
|
||||
lavenderblush
|
||||
misty-rose
|
||||
mistyrose
|
||||
white
|
||||
black
|
||||
dark-slate-gray
|
||||
darkslategray
|
||||
dark-slate-grey
|
||||
darkslategrey
|
||||
dim-gray
|
||||
dimgray
|
||||
dim-grey
|
||||
dimgrey
|
||||
slate-gray
|
||||
slategray
|
||||
slate-grey
|
||||
slategrey
|
||||
light-slate-gray
|
||||
lightslategray
|
||||
light-slate-grey
|
||||
lightslategrey
|
||||
gray
|
||||
grey
|
||||
xray
|
||||
x11gray
|
||||
xrey
|
||||
x11grey
|
||||
web-gray
|
||||
webgray
|
||||
web-grey
|
||||
webgrey
|
||||
light-grey
|
||||
lightgrey
|
||||
light-gray
|
||||
lightgray
|
||||
midnight-blue
|
||||
midnightblue
|
||||
navy
|
||||
navy-blue
|
||||
navyblue
|
||||
cornflower-blue
|
||||
cornflowerblue
|
||||
dark-slate-blue
|
||||
darkslateblue
|
||||
slate-blue
|
||||
slateblue
|
||||
medium-slate-blue
|
||||
mediumslateblue
|
||||
light-slate-blue
|
||||
lightslateblue
|
||||
medium-blue
|
||||
mediumblue
|
||||
royal-blue
|
||||
royalblue
|
||||
blue
|
||||
dodger-blue
|
||||
dodgerblue
|
||||
deep-sky-blue
|
||||
deepskyblue
|
||||
sky-blue
|
||||
skyblue
|
||||
light-sky-blue
|
||||
lightskyblue
|
||||
steel-blue
|
||||
steelblue
|
||||
light-steel-blue
|
||||
lightsteelblue
|
||||
light-blue
|
||||
lightblue
|
||||
powder-blue
|
||||
powderblue
|
||||
pale-turquoise
|
||||
paleturquoise
|
||||
dark-turquoise
|
||||
darkturquoise
|
||||
medium-turquoise
|
||||
mediumturquoise
|
||||
turquoise
|
||||
cyan
|
||||
aqua
|
||||
light-cyan
|
||||
lightcyan
|
||||
cadet-blue
|
||||
cadetblue
|
||||
medium-aquamarine
|
||||
mediumaquamarine
|
||||
aquamarine
|
||||
dark-green
|
||||
darkgreen
|
||||
dark-olive-green
|
||||
darkolivegreen
|
||||
dark-sea-green
|
||||
darkseagreen
|
||||
sea-green
|
||||
seagreen
|
||||
medium-sea-green
|
||||
mediumseagreen
|
||||
light-sea-green
|
||||
lightseagreen
|
||||
pale-green
|
||||
palegreen
|
||||
spring-green
|
||||
springgreen
|
||||
lawn-green
|
||||
lawngreen
|
||||
green
|
||||
lime
|
||||
xreen
|
||||
x11green
|
||||
web-green
|
||||
webgreen
|
||||
chartreuse
|
||||
medium-spring-green
|
||||
mediumspringgreen
|
||||
green-yellow
|
||||
greenyellow
|
||||
lime-green
|
||||
limegreen
|
||||
yellow-green
|
||||
yellowgreen
|
||||
forest-green
|
||||
forestgreen
|
||||
olive-drab
|
||||
olivedrab
|
||||
dark-khaki
|
||||
darkkhaki
|
||||
khaki
|
||||
pale-goldenrod
|
||||
palegoldenrod
|
||||
light-goldenrod-yellow
|
||||
lightgoldenrodyellow
|
||||
light-yellow
|
||||
lightyellow
|
||||
yellow
|
||||
gold
|
||||
light-goldenrod
|
||||
lightgoldenrod
|
||||
goldenrod
|
||||
dark-goldenrod
|
||||
darkgoldenrod
|
||||
rosy-brown
|
||||
rosybrown
|
||||
indian-red
|
||||
indianred
|
||||
saddle-brown
|
||||
saddlebrown
|
||||
sienna
|
||||
peru
|
||||
burlywood
|
||||
beige
|
||||
wheat
|
||||
sandy-brown
|
||||
sandybrown
|
||||
tan
|
||||
chocolate
|
||||
firebrick
|
||||
brown
|
||||
dark-salmon
|
||||
darksalmon
|
||||
salmon
|
||||
light-salmon
|
||||
lightsalmon
|
||||
orange
|
||||
dark-orange
|
||||
darkorange
|
||||
coral
|
||||
light-coral
|
||||
lightcoral
|
||||
tomato
|
||||
orange-red
|
||||
orangered
|
||||
red
|
||||
hot-pink
|
||||
hotpink
|
||||
deep-pink
|
||||
deeppink
|
||||
pink
|
||||
light-pink
|
||||
lightpink
|
||||
pale-violet-red
|
||||
palevioletred
|
||||
maroon
|
||||
xaroon
|
||||
x11maroon
|
||||
web-maroon
|
||||
webmaroon
|
||||
medium-violet-red
|
||||
mediumvioletred
|
||||
violet-red
|
||||
violetred
|
||||
magenta
|
||||
fuchsia
|
||||
violet
|
||||
plum
|
||||
orchid
|
||||
medium-orchid
|
||||
mediumorchid
|
||||
dark-orchid
|
||||
darkorchid
|
||||
dark-violet
|
||||
darkviolet
|
||||
blue-violet
|
||||
blueviolet
|
||||
purple
|
||||
xurple
|
||||
x11purple
|
||||
web-purple
|
||||
webpurple
|
||||
medium-purple
|
||||
mediumpurple
|
||||
thistle
|
||||
snow1
|
||||
snow2
|
||||
snow3
|
||||
snow4
|
||||
seashell1
|
||||
seashell2
|
||||
seashell3
|
||||
seashell4
|
||||
antiquewhite1
|
||||
antiquewhite2
|
||||
antiquewhite3
|
||||
antiquewhite4
|
||||
bisque1
|
||||
bisque2
|
||||
bisque3
|
||||
bisque4
|
||||
peachpuff1
|
||||
peachpuff2
|
||||
peachpuff3
|
||||
peachpuff4
|
||||
navajowhite1
|
||||
navajowhite2
|
||||
navajowhite3
|
||||
navajowhite4
|
||||
lemonchiffon1
|
||||
lemonchiffon2
|
||||
lemonchiffon3
|
||||
lemonchiffon4
|
||||
cornsilk1
|
||||
cornsilk2
|
||||
cornsilk3
|
||||
cornsilk4
|
||||
ivory1
|
||||
ivory2
|
||||
ivory3
|
||||
ivory4
|
||||
honeydew1
|
||||
honeydew2
|
||||
honeydew3
|
||||
honeydew4
|
||||
lavenderblush1
|
||||
lavenderblush2
|
||||
lavenderblush3
|
||||
lavenderblush4
|
||||
mistyrose1
|
||||
mistyrose2
|
||||
mistyrose3
|
||||
mistyrose4
|
||||
azure1
|
||||
azure2
|
||||
azure3
|
||||
azure4
|
||||
slateblue1
|
||||
slateblue2
|
||||
slateblue3
|
||||
slateblue4
|
||||
royalblue1
|
||||
royalblue2
|
||||
royalblue3
|
||||
royalblue4
|
||||
blue1
|
||||
blue2
|
||||
blue3
|
||||
blue4
|
||||
dodgerblue1
|
||||
dodgerblue2
|
||||
dodgerblue3
|
||||
dodgerblue4
|
||||
steelblue1
|
||||
steelblue2
|
||||
steelblue3
|
||||
steelblue4
|
||||
deepskyblue1
|
||||
deepskyblue2
|
||||
deepskyblue3
|
||||
deepskyblue4
|
||||
skyblue1
|
||||
skyblue2
|
||||
skyblue3
|
||||
skyblue4
|
||||
lightskyblue1
|
||||
lightskyblue2
|
||||
lightskyblue3
|
||||
lightskyblue4
|
||||
slategray1
|
||||
slategray2
|
||||
slategray3
|
||||
slategray4
|
||||
lightsteelblue1
|
||||
lightsteelblue2
|
||||
lightsteelblue3
|
||||
lightsteelblue4
|
||||
lightblue1
|
||||
lightblue2
|
||||
lightblue3
|
||||
lightblue4
|
||||
lightcyan1
|
||||
lightcyan2
|
||||
lightcyan3
|
||||
lightcyan4
|
||||
paleturquoise1
|
||||
paleturquoise2
|
||||
paleturquoise3
|
||||
paleturquoise4
|
||||
cadetblue1
|
||||
cadetblue2
|
||||
cadetblue3
|
||||
cadetblue4
|
||||
turquoise1
|
||||
turquoise2
|
||||
turquoise3
|
||||
turquoise4
|
||||
cyan1
|
||||
cyan2
|
||||
cyan3
|
||||
cyan4
|
||||
darkslategray1
|
||||
darkslategray2
|
||||
darkslategray3
|
||||
darkslategray4
|
||||
aquamarine1
|
||||
aquamarine2
|
||||
aquamarine3
|
||||
aquamarine4
|
||||
darkseagreen1
|
||||
darkseagreen2
|
||||
darkseagreen3
|
||||
darkseagreen4
|
||||
seagreen1
|
||||
seagreen2
|
||||
seagreen3
|
||||
seagreen4
|
||||
palegreen1
|
||||
palegreen2
|
||||
palegreen3
|
||||
palegreen4
|
||||
springgreen1
|
||||
springgreen2
|
||||
springgreen3
|
||||
springgreen4
|
||||
green1
|
||||
green2
|
||||
green3
|
||||
green4
|
||||
chartreuse1
|
||||
chartreuse2
|
||||
chartreuse3
|
||||
chartreuse4
|
||||
olivedrab1
|
||||
olivedrab2
|
||||
olivedrab3
|
||||
olivedrab4
|
||||
darkolivegreen1
|
||||
darkolivegreen2
|
||||
darkolivegreen3
|
||||
darkolivegreen4
|
||||
khaki1
|
||||
khaki2
|
||||
khaki3
|
||||
khaki4
|
||||
lightgoldenrod1
|
||||
lightgoldenrod2
|
||||
lightgoldenrod3
|
||||
lightgoldenrod4
|
||||
lightyellow1
|
||||
lightyellow2
|
||||
lightyellow3
|
||||
lightyellow4
|
||||
yellow1
|
||||
yellow2
|
||||
yellow3
|
||||
yellow4
|
||||
gold1
|
||||
gold2
|
||||
gold3
|
||||
gold4
|
||||
goldenrod1
|
||||
goldenrod2
|
||||
goldenrod3
|
||||
goldenrod4
|
||||
darkgoldenrod1
|
||||
darkgoldenrod2
|
||||
darkgoldenrod3
|
||||
darkgoldenrod4
|
||||
rosybrown1
|
||||
rosybrown2
|
||||
rosybrown3
|
||||
rosybrown4
|
||||
indianred1
|
||||
indianred2
|
||||
indianred3
|
||||
indianred4
|
||||
sienna1
|
||||
sienna2
|
||||
sienna3
|
||||
sienna4
|
||||
burlywood1
|
||||
burlywood2
|
||||
burlywood3
|
||||
burlywood4
|
||||
wheat1
|
||||
wheat2
|
||||
wheat3
|
||||
wheat4
|
||||
tan1
|
||||
tan2
|
||||
tan3
|
||||
tan4
|
||||
chocolate1
|
||||
chocolate2
|
||||
chocolate3
|
||||
chocolate4
|
||||
firebrick1
|
||||
firebrick2
|
||||
firebrick3
|
||||
firebrick4
|
||||
brown1
|
||||
brown2
|
||||
brown3
|
||||
brown4
|
||||
salmon1
|
||||
salmon2
|
||||
salmon3
|
||||
salmon4
|
||||
lightsalmon1
|
||||
lightsalmon2
|
||||
lightsalmon3
|
||||
lightsalmon4
|
||||
orange1
|
||||
orange2
|
||||
orange3
|
||||
orange4
|
||||
darkorange1
|
||||
darkorange2
|
||||
darkorange3
|
||||
darkorange4
|
||||
coral1
|
||||
coral2
|
||||
coral3
|
||||
coral4
|
||||
tomato1
|
||||
tomato2
|
||||
tomato3
|
||||
tomato4
|
||||
orangered1
|
||||
orangered2
|
||||
orangered3
|
||||
orangered4
|
||||
red1
|
||||
red2
|
||||
red3
|
||||
red4
|
||||
deeppink1
|
||||
deeppink2
|
||||
deeppink3
|
||||
deeppink4
|
||||
hotpink1
|
||||
hotpink2
|
||||
hotpink3
|
||||
hotpink4
|
||||
pink1
|
||||
pink2
|
||||
pink3
|
||||
pink4
|
||||
lightpink1
|
||||
lightpink2
|
||||
lightpink3
|
||||
lightpink4
|
||||
palevioletred1
|
||||
palevioletred2
|
||||
palevioletred3
|
||||
palevioletred4
|
||||
maroon1
|
||||
maroon2
|
||||
maroon3
|
||||
maroon4
|
||||
violetred1
|
||||
violetred2
|
||||
violetred3
|
||||
violetred4
|
||||
magenta1
|
||||
magenta2
|
||||
magenta3
|
||||
magenta4
|
||||
orchid1
|
||||
orchid2
|
||||
orchid3
|
||||
orchid4
|
||||
plum1
|
||||
plum2
|
||||
plum3
|
||||
plum4
|
||||
mediumorchid1
|
||||
mediumorchid2
|
||||
mediumorchid3
|
||||
mediumorchid4
|
||||
darkorchid1
|
||||
darkorchid2
|
||||
darkorchid3
|
||||
darkorchid4
|
||||
purple1
|
||||
purple2
|
||||
purple3
|
||||
purple4
|
||||
mediumpurple1
|
||||
mediumpurple2
|
||||
mediumpurple3
|
||||
mediumpurple4
|
||||
thistle1
|
||||
thistle2
|
||||
thistle3
|
||||
thistle4
|
||||
gray0
|
||||
grey0
|
||||
gray1
|
||||
grey1
|
||||
gray2
|
||||
grey2
|
||||
gray3
|
||||
grey3
|
||||
gray4
|
||||
grey4
|
||||
gray5
|
||||
grey5
|
||||
gray6
|
||||
grey6
|
||||
gray7
|
||||
grey7
|
||||
gray8
|
||||
grey8
|
||||
gray9
|
||||
grey9
|
||||
gray10
|
||||
grey10
|
||||
gray11
|
||||
grey11
|
||||
gray12
|
||||
grey12
|
||||
gray13
|
||||
grey13
|
||||
gray14
|
||||
grey14
|
||||
gray15
|
||||
grey15
|
||||
gray16
|
||||
grey16
|
||||
gray17
|
||||
grey17
|
||||
gray18
|
||||
grey18
|
||||
gray19
|
||||
grey19
|
||||
gray20
|
||||
grey20
|
||||
gray21
|
||||
grey21
|
||||
gray22
|
||||
grey22
|
||||
gray23
|
||||
grey23
|
||||
gray24
|
||||
grey24
|
||||
gray25
|
||||
grey25
|
||||
gray26
|
||||
grey26
|
||||
gray27
|
||||
grey27
|
||||
gray28
|
||||
grey28
|
||||
gray29
|
||||
grey29
|
||||
gray30
|
||||
grey30
|
||||
gray31
|
||||
grey31
|
||||
gray32
|
||||
grey32
|
||||
gray33
|
||||
grey33
|
||||
gray34
|
||||
grey34
|
||||
gray35
|
||||
grey35
|
||||
gray36
|
||||
grey36
|
||||
gray37
|
||||
grey37
|
||||
gray38
|
||||
grey38
|
||||
gray39
|
||||
grey39
|
||||
gray40
|
||||
grey40
|
||||
gray41
|
||||
grey41
|
||||
gray42
|
||||
grey42
|
||||
gray43
|
||||
grey43
|
||||
gray44
|
||||
grey44
|
||||
gray45
|
||||
grey45
|
||||
gray46
|
||||
grey46
|
||||
gray47
|
||||
grey47
|
||||
gray48
|
||||
grey48
|
||||
gray49
|
||||
grey49
|
||||
gray50
|
||||
grey50
|
||||
gray51
|
||||
grey51
|
||||
gray52
|
||||
grey52
|
||||
gray53
|
||||
grey53
|
||||
gray54
|
||||
grey54
|
||||
gray55
|
||||
grey55
|
||||
gray56
|
||||
grey56
|
||||
gray57
|
||||
grey57
|
||||
gray58
|
||||
grey58
|
||||
gray59
|
||||
grey59
|
||||
gray60
|
||||
grey60
|
||||
gray61
|
||||
grey61
|
||||
gray62
|
||||
grey62
|
||||
gray63
|
||||
grey63
|
||||
gray64
|
||||
grey64
|
||||
gray65
|
||||
grey65
|
||||
gray66
|
||||
grey66
|
||||
gray67
|
||||
grey67
|
||||
gray68
|
||||
grey68
|
||||
gray69
|
||||
grey69
|
||||
gray70
|
||||
grey70
|
||||
gray71
|
||||
grey71
|
||||
gray72
|
||||
grey72
|
||||
gray73
|
||||
grey73
|
||||
gray74
|
||||
grey74
|
||||
gray75
|
||||
grey75
|
||||
gray76
|
||||
grey76
|
||||
gray77
|
||||
grey77
|
||||
gray78
|
||||
grey78
|
||||
gray79
|
||||
grey79
|
||||
gray80
|
||||
grey80
|
||||
gray81
|
||||
grey81
|
||||
gray82
|
||||
grey82
|
||||
gray83
|
||||
grey83
|
||||
gray84
|
||||
grey84
|
||||
gray85
|
||||
grey85
|
||||
gray86
|
||||
grey86
|
||||
gray87
|
||||
grey87
|
||||
gray88
|
||||
grey88
|
||||
gray89
|
||||
grey89
|
||||
gray90
|
||||
grey90
|
||||
gray91
|
||||
grey91
|
||||
gray92
|
||||
grey92
|
||||
gray93
|
||||
grey93
|
||||
gray94
|
||||
grey94
|
||||
gray95
|
||||
grey95
|
||||
gray96
|
||||
grey96
|
||||
gray97
|
||||
grey97
|
||||
gray98
|
||||
grey98
|
||||
gray99
|
||||
grey99
|
||||
gray100
|
||||
grey100
|
||||
dark-grey
|
||||
darkgrey
|
||||
dark-gray
|
||||
darkgray
|
||||
dark-blue
|
||||
darkblue
|
||||
dark-cyan
|
||||
darkcyan
|
||||
dark-magenta
|
||||
darkmagenta
|
||||
dark-red
|
||||
darkred
|
||||
light-green
|
||||
lightgreen
|
||||
crimson
|
||||
indigo
|
||||
olive
|
||||
rebecca-purple
|
||||
rebeccapurple
|
||||
silver
|
||||
teal
|
479851
.github/actions/spelling/dictionary/dictionary.txt
vendored
479851
.github/actions/spelling/dictionary/dictionary.txt
vendored
File diff suppressed because it is too large
Load diff
20
.github/actions/spelling/excludes.txt
vendored
20
.github/actions/spelling/excludes.txt
vendored
|
@ -1,3 +1,4 @@
|
|||
(?:(?i)\.png$)
|
||||
(?:^|/)dirs$
|
||||
(?:^|/)go\.mod$
|
||||
(?:^|/)go\.sum$
|
||||
|
@ -35,7 +36,6 @@ SUMS$
|
|||
\.pbxproj$
|
||||
\.pdf$
|
||||
\.pem$
|
||||
(?:(?i)\.png$)
|
||||
\.psd$
|
||||
\.runsettings$
|
||||
\.sig$
|
||||
|
@ -54,14 +54,26 @@ SUMS$
|
|||
\.zip$
|
||||
^consolegit2gitfilters\.json$
|
||||
^dep/
|
||||
^oss/
|
||||
^doc/reference/master-sequence-list.csv$
|
||||
^doc/reference/UTF8-torture-test\.txt$
|
||||
^oss/
|
||||
^src/host/ft_uia/run\.bat$
|
||||
^src/host/runft\.bat$
|
||||
^src/host/runut\.bat$
|
||||
^src/interactivity/onecore/BgfxEngine\.
|
||||
^src/renderer/wddmcon/WddmConRenderer\.
|
||||
^src/terminal/adapter/ut_adapter/run\.bat$
|
||||
^src/terminal/parser/delfuzzpayload\.bat$
|
||||
^src/terminal/parser/ft_fuzzer/run\.bat$
|
||||
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
|
||||
^src/types/ut_types/UtilsTests.cpp$
|
||||
^src/terminal/parser/ft_fuzzwrapper/run\.bat$
|
||||
^src/terminal/parser/ut_parser/run\.bat$
|
||||
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
|
||||
^src/tools/lnkd/lnkd\.bat$
|
||||
^src/tools/pixels/pixels\.bat$
|
||||
^src/tools/texttests/fira\.txt$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^src/types/ut_types/UtilsTests.cpp$
|
||||
^\.github/actions/spelling/
|
||||
^\.gitignore$
|
||||
^doc/reference/master-sequence-list.csv$
|
||||
^\XamlStyler.json$
|
||||
|
|
16
.github/actions/spelling/expect/alphabet.txt
vendored
16
.github/actions/spelling/expect/alphabet.txt
vendored
|
@ -1,4 +1,9 @@
|
|||
AAAa
|
||||
AAAAA
|
||||
AAAAAAAAAAAAA
|
||||
AAAAAABBBBBBCCC
|
||||
AAAAABBBBBBCCC
|
||||
abcd
|
||||
abcd
|
||||
abcde
|
||||
abcdef
|
||||
|
@ -6,12 +11,20 @@ ABCDEFG
|
|||
ABCDEFGH
|
||||
ABCDEFGHIJ
|
||||
abcdefghijk
|
||||
ABCDEFGHIJKLMNO
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
ABCG
|
||||
ABE
|
||||
BBGGRR
|
||||
abf
|
||||
BBBBB
|
||||
BBBBBBBB
|
||||
BBBBBBBBBBBBBBDDDD
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBGGRR
|
||||
CCE
|
||||
EFG
|
||||
EFGh
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
|
@ -32,3 +45,4 @@ ZYXWVUT
|
|||
ZZBBZ
|
||||
ZZZBB
|
||||
ZZZBZ
|
||||
ZZZZZ
|
||||
|
|
187
.github/actions/spelling/expect/expect.txt
vendored
187
.github/actions/spelling/expect/expect.txt
vendored
|
@ -1,13 +1,4 @@
|
|||
AAAAAABBBBBBCCC
|
||||
AAAAABBBBBBBCCC
|
||||
AAAAABBBBBBCCC
|
||||
AAAAABCCCCCCCCC
|
||||
AAAAADCCCCCCCCC
|
||||
ABANDONFONT
|
||||
abcd
|
||||
ABCDEFGHIJKLMNO
|
||||
ABCG
|
||||
abf
|
||||
abgr
|
||||
abi
|
||||
ACCESSTOKEN
|
||||
|
@ -21,13 +12,13 @@ ACTCTXW
|
|||
activatable
|
||||
ACTIVEBORDER
|
||||
ACTIVECAPTION
|
||||
adaa
|
||||
ADDALIAS
|
||||
ADDB
|
||||
ADDREF
|
||||
addressof
|
||||
ADDSTRING
|
||||
ADDTOOL
|
||||
aef
|
||||
AEnd
|
||||
AFew
|
||||
AFill
|
||||
|
@ -45,7 +36,6 @@ ALTF
|
|||
ALTNUMPAD
|
||||
ALWAYSTIP
|
||||
amd
|
||||
ansd
|
||||
ansicode
|
||||
ansicpg
|
||||
ANSISYS
|
||||
|
@ -61,7 +51,6 @@ APCs
|
|||
api
|
||||
APIENTRY
|
||||
apiset
|
||||
apos
|
||||
APPBARDATA
|
||||
appconsult
|
||||
APPICON
|
||||
|
@ -73,10 +62,6 @@ APPLMODAL
|
|||
appmodel
|
||||
apps
|
||||
APPWINDOW
|
||||
appx
|
||||
appxbundle
|
||||
appxerror
|
||||
appxmanifest
|
||||
APrep
|
||||
apsect
|
||||
APSTUDIO
|
||||
|
@ -98,7 +83,6 @@ ASingle
|
|||
asm
|
||||
asmv
|
||||
asmx
|
||||
aspnet
|
||||
aspx
|
||||
astextplain
|
||||
AStomps
|
||||
|
@ -123,7 +107,6 @@ AVerify
|
|||
AVI
|
||||
awch
|
||||
azuredevopspodcast
|
||||
azurewebsites
|
||||
azzle
|
||||
backend
|
||||
backgrounded
|
||||
|
@ -134,11 +117,8 @@ backstory
|
|||
Batang
|
||||
baz
|
||||
Bazz
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBDM
|
||||
bbwe
|
||||
bc
|
||||
bcount
|
||||
bcrypt
|
||||
bcx
|
||||
|
@ -170,10 +150,11 @@ blog
|
|||
Blt
|
||||
BLUESCROLL
|
||||
bmp
|
||||
BODGY
|
||||
BOLDFONT
|
||||
BOOLIFY
|
||||
bools
|
||||
boostorg
|
||||
Bopomofo
|
||||
Borland
|
||||
BOTTOMLEFT
|
||||
BOTTOMRIGHT
|
||||
|
@ -185,7 +166,6 @@ branchconfig
|
|||
BRK
|
||||
Browsable
|
||||
bsearch
|
||||
BSODs
|
||||
bstr
|
||||
BTNFACE
|
||||
buf
|
||||
|
@ -200,9 +180,9 @@ BValue
|
|||
byref
|
||||
bytearray
|
||||
bytebuffer
|
||||
Cac
|
||||
cac
|
||||
cacafire
|
||||
callee
|
||||
cang
|
||||
capslock
|
||||
CARETBLINKINGENABLED
|
||||
CARRIAGERETURN
|
||||
|
@ -259,7 +239,6 @@ Childitem
|
|||
chk
|
||||
chrono
|
||||
CHT
|
||||
ci
|
||||
Cic
|
||||
cjk
|
||||
ckuehl
|
||||
|
@ -300,7 +279,9 @@ codepage
|
|||
codepath
|
||||
codepoint
|
||||
codeproject
|
||||
COINIT
|
||||
coffgroup
|
||||
coffgrp
|
||||
coinit
|
||||
COLLECTIONURI
|
||||
colorizing
|
||||
colororacle
|
||||
|
@ -352,7 +333,6 @@ conlibk
|
|||
conmsgl
|
||||
CONNECTINFO
|
||||
CONOUT
|
||||
conpixels
|
||||
conprops
|
||||
conpropsp
|
||||
conpty
|
||||
|
@ -393,7 +373,7 @@ cout
|
|||
CPG
|
||||
cpinfo
|
||||
CPINFOEX
|
||||
cplinfo
|
||||
CPLINFO
|
||||
cplusplus
|
||||
cpp
|
||||
CPPCORECHECK
|
||||
|
@ -413,7 +393,7 @@ cref
|
|||
crend
|
||||
CRLFs
|
||||
crloew
|
||||
crt
|
||||
Crt
|
||||
CRTLIBS
|
||||
csbi
|
||||
csbiex
|
||||
|
@ -456,13 +436,11 @@ CURSORCOLOR
|
|||
CURSORSIZE
|
||||
CURSORTYPE
|
||||
CUU
|
||||
cw
|
||||
Cwa
|
||||
cwch
|
||||
cwchar
|
||||
cwctype
|
||||
cwd
|
||||
cx
|
||||
cxcy
|
||||
CXFRAME
|
||||
CXFULLSCREEN
|
||||
|
@ -485,11 +463,11 @@ CYSIZEFRAME
|
|||
CYSMICON
|
||||
CYVIRTUALSCREEN
|
||||
CYVSCROLL
|
||||
dahall
|
||||
dai
|
||||
DATABLOCK
|
||||
DATAVIEW
|
||||
DATAW
|
||||
datetime
|
||||
DBatch
|
||||
dbcs
|
||||
DBCSCHAR
|
||||
|
@ -518,6 +496,7 @@ dealloc
|
|||
Debian
|
||||
debolden
|
||||
debounce
|
||||
debugtype
|
||||
DECALN
|
||||
DECANM
|
||||
DECAUPSS
|
||||
|
@ -573,7 +552,6 @@ defaultsettings
|
|||
DEFAULTTONEAREST
|
||||
DEFAULTTONULL
|
||||
DEFAULTTOPRIMARY
|
||||
DEFCON
|
||||
defectdefs
|
||||
DEFERERASE
|
||||
deff
|
||||
|
@ -601,16 +579,13 @@ desktopwindowxamlsource
|
|||
dest
|
||||
DESTINATIONNAME
|
||||
devblogs
|
||||
developercommunity
|
||||
devicecode
|
||||
devicefamily
|
||||
devops
|
||||
Dext
|
||||
df
|
||||
DFactory
|
||||
DFF
|
||||
DFMT
|
||||
dh
|
||||
dhandler
|
||||
dialogbox
|
||||
diffing
|
||||
|
@ -668,7 +643,7 @@ DUNICODE
|
|||
DUNIT
|
||||
dup'ed
|
||||
dvi
|
||||
dw
|
||||
dwl
|
||||
DWLP
|
||||
dwm
|
||||
dwmapi
|
||||
|
@ -680,9 +655,9 @@ dxgidwm
|
|||
dxinterop
|
||||
dxttbmp
|
||||
eachother
|
||||
eae
|
||||
eaf
|
||||
EASTEUROPE
|
||||
eb
|
||||
ECH
|
||||
echokey
|
||||
ecount
|
||||
|
@ -696,12 +671,12 @@ edu
|
|||
Efast
|
||||
EHsc
|
||||
EJO
|
||||
EK
|
||||
ELEMENTNOTAVAILABLE
|
||||
elems
|
||||
elif
|
||||
elseif
|
||||
emacs
|
||||
emplate
|
||||
EMPTYBOX
|
||||
enabledelayedexpansion
|
||||
endian
|
||||
|
@ -729,7 +704,6 @@ etcoreapp
|
|||
ETW
|
||||
ETX
|
||||
EUDC
|
||||
ev
|
||||
EVENTID
|
||||
eventing
|
||||
everytime
|
||||
|
@ -742,7 +716,6 @@ executables
|
|||
executionengine
|
||||
exemain
|
||||
EXETYPE
|
||||
exeuwp
|
||||
exewin
|
||||
exitwin
|
||||
expectedinput
|
||||
|
@ -752,6 +725,7 @@ EXSTYLE
|
|||
EXTENDEDEDITKEY
|
||||
EXTKEY
|
||||
EXTTEXTOUT
|
||||
fabricbot
|
||||
facename
|
||||
FACENODE
|
||||
FACESIZE
|
||||
|
@ -766,10 +740,8 @@ fclose
|
|||
fcntl
|
||||
fdc
|
||||
FDD
|
||||
fde
|
||||
fdopen
|
||||
fdw
|
||||
fea
|
||||
fesb
|
||||
FFDE
|
||||
FFrom
|
||||
|
@ -786,6 +758,7 @@ FILESYSPATH
|
|||
filesystem
|
||||
FILETIME
|
||||
FILETYPE
|
||||
fileurl
|
||||
FILEW
|
||||
FILLATTR
|
||||
FILLCONSOLEOUTPUT
|
||||
|
@ -804,7 +777,6 @@ flyout
|
|||
fmodern
|
||||
fmtarg
|
||||
fmtid
|
||||
fmtlib
|
||||
FOLDERID
|
||||
FONTCHANGE
|
||||
fontdlg
|
||||
|
@ -853,13 +825,13 @@ fwdecl
|
|||
fwe
|
||||
fwlink
|
||||
GAUSSIAN
|
||||
gb
|
||||
gci
|
||||
gcx
|
||||
gcy
|
||||
gdi
|
||||
gdip
|
||||
gdirenderer
|
||||
GENPROFILE
|
||||
geopol
|
||||
GETALIAS
|
||||
GETALIASES
|
||||
|
@ -917,7 +889,6 @@ getwriter
|
|||
GFEh
|
||||
Gfun
|
||||
gfx
|
||||
gh
|
||||
GHIJK
|
||||
GHIJKL
|
||||
GHIJKLM
|
||||
|
@ -934,7 +905,6 @@ gonce
|
|||
Google
|
||||
goutput
|
||||
GPUs
|
||||
Gravell's
|
||||
grayscale
|
||||
GREENSCROLL
|
||||
Grehan
|
||||
|
@ -967,7 +937,6 @@ HBMMENU
|
|||
hbmp
|
||||
hbr
|
||||
hbrush
|
||||
hc
|
||||
HCmd
|
||||
hdc
|
||||
hdr
|
||||
|
@ -977,8 +946,8 @@ HEIGHTSCROLL
|
|||
hfile
|
||||
hfont
|
||||
hglobal
|
||||
hh
|
||||
hhh
|
||||
HHmm
|
||||
hhook
|
||||
hhx
|
||||
HIBYTE
|
||||
|
@ -993,7 +962,6 @@ HISTORYNODUP
|
|||
HISTORYSIZE
|
||||
hittest
|
||||
HIWORD
|
||||
hk
|
||||
HKCU
|
||||
hkey
|
||||
hkl
|
||||
|
@ -1009,9 +977,10 @@ horiz
|
|||
HORZ
|
||||
hostable
|
||||
hostlib
|
||||
Hostx
|
||||
HPA
|
||||
HPAINTBUFFER
|
||||
HPCON
|
||||
hpcon
|
||||
hpj
|
||||
hpp
|
||||
HPR
|
||||
|
@ -1025,15 +994,20 @@ hsl
|
|||
hstr
|
||||
hstring
|
||||
hsv
|
||||
HTBOTTOMLEFT
|
||||
HTBOTTOMRIGHT
|
||||
HTCAPTION
|
||||
HTCLIENT
|
||||
HTLEFT
|
||||
htm
|
||||
HTMAXBUTTON
|
||||
HTMINBUTTON
|
||||
html
|
||||
HTMLTo
|
||||
HTRIGHT
|
||||
HTTOP
|
||||
hu
|
||||
HTTOPLEFT
|
||||
HTTOPRIGHT
|
||||
hungapp
|
||||
HVP
|
||||
hwheel
|
||||
|
@ -1129,6 +1103,7 @@ INPUTPROCESSORPROFILE
|
|||
inputrc
|
||||
Inputreadhandledata
|
||||
INSERTMODE
|
||||
installationpath
|
||||
intellisense
|
||||
INTERACTIVITYBASE
|
||||
INTERCEPTCOPYPASTE
|
||||
|
@ -1161,7 +1136,6 @@ IRenderer
|
|||
IScheme
|
||||
ISelection
|
||||
IShell
|
||||
isocpp
|
||||
issuecomment
|
||||
IState
|
||||
IStoryboard
|
||||
|
@ -1198,6 +1172,7 @@ jpeg
|
|||
jpg
|
||||
JPN
|
||||
json
|
||||
jsonc
|
||||
jsoncpp
|
||||
jsprovider
|
||||
jumplist
|
||||
|
@ -1211,7 +1186,6 @@ kcub
|
|||
kcud
|
||||
kcuf
|
||||
kcuu
|
||||
Kd
|
||||
kernelbase
|
||||
kernelbasestaging
|
||||
KEYBDINPUT
|
||||
|
@ -1226,19 +1200,15 @@ Keymapping
|
|||
keyscan
|
||||
keystate
|
||||
keyup
|
||||
KF
|
||||
khome
|
||||
KILLACTIVE
|
||||
KILLFOCUS
|
||||
kinda
|
||||
KJ
|
||||
KLF
|
||||
KLMNO
|
||||
KLMNOPQRST
|
||||
KLMNOPQRSTQQQQQ
|
||||
KU
|
||||
KVM
|
||||
KX
|
||||
langid
|
||||
LANGUAGELIST
|
||||
lasterror
|
||||
|
@ -1276,9 +1246,8 @@ listbox
|
|||
listproperties
|
||||
listptr
|
||||
listptrsize
|
||||
lk
|
||||
lld
|
||||
llvm
|
||||
LLVM
|
||||
llx
|
||||
LMENU
|
||||
LMNOP
|
||||
|
@ -1398,7 +1367,6 @@ MEASUREITEM
|
|||
megamix
|
||||
memallocator
|
||||
memcmp
|
||||
memcopy
|
||||
memcpy
|
||||
memmove
|
||||
memset
|
||||
|
@ -1423,7 +1391,6 @@ mingw
|
|||
minimizeall
|
||||
minkernel
|
||||
MINMAXINFO
|
||||
mintty
|
||||
minwin
|
||||
minwindef
|
||||
Mip
|
||||
|
@ -1431,6 +1398,7 @@ mkdir
|
|||
MMBB
|
||||
mmcc
|
||||
MMCPL
|
||||
MMdd
|
||||
mmsystem
|
||||
MNC
|
||||
MNOPQ
|
||||
|
@ -1470,7 +1438,7 @@ MSIL
|
|||
msix
|
||||
msrc
|
||||
msvcrt
|
||||
msvcrtd
|
||||
MSVCRTD
|
||||
MSVS
|
||||
msys
|
||||
msysgit
|
||||
|
@ -1496,7 +1464,6 @@ namestream
|
|||
nano
|
||||
natvis
|
||||
nbsp
|
||||
Nc
|
||||
NCCALCSIZE
|
||||
NCCREATE
|
||||
NCLBUTTONDOWN
|
||||
|
@ -1619,11 +1586,9 @@ numlock
|
|||
numpad
|
||||
NUMSCROLL
|
||||
nupkg
|
||||
NVDA
|
||||
nuspec
|
||||
NVIDIA
|
||||
NVR
|
||||
Nx
|
||||
oa
|
||||
OACR
|
||||
oauth
|
||||
objbase
|
||||
|
@ -1635,8 +1600,6 @@ oemcp
|
|||
OEMFONT
|
||||
OEMFORMAT
|
||||
offboarded
|
||||
OI
|
||||
Oj
|
||||
OLEAUT
|
||||
OLECHAR
|
||||
onecore
|
||||
|
@ -1649,19 +1612,17 @@ onecoreuuid
|
|||
ONECOREWINDOWS
|
||||
onehalf
|
||||
ONLCR
|
||||
Oo
|
||||
openconsoleproxy
|
||||
openbash
|
||||
opencode
|
||||
opencon
|
||||
openconsole
|
||||
openconsoleproxy
|
||||
OPENIF
|
||||
OPENLINK
|
||||
openps
|
||||
opensource
|
||||
openvt
|
||||
openxmlformats
|
||||
oq
|
||||
ORIGINALFILENAME
|
||||
osc
|
||||
OSCBG
|
||||
|
@ -1679,7 +1640,6 @@ osign
|
|||
oss
|
||||
ostream
|
||||
ostringstream
|
||||
Ou
|
||||
ouicompat
|
||||
outdir
|
||||
outfile
|
||||
|
@ -1688,7 +1648,6 @@ OUTOFCONTEXT
|
|||
OUTOFMEMORY
|
||||
outout
|
||||
Outptr
|
||||
Ov
|
||||
OVERLAPPEDWINDOW
|
||||
OWNDC
|
||||
OWNERDRAWFIXED
|
||||
|
@ -1708,7 +1667,6 @@ passthrough
|
|||
PATCOPY
|
||||
pathcch
|
||||
PATTERNID
|
||||
pb
|
||||
PBOOL
|
||||
PBYTE
|
||||
pcat
|
||||
|
@ -1762,7 +1720,11 @@ PFNCONSOLECREATEIOTHREAD
|
|||
PFONT
|
||||
PFONTENUMDATA
|
||||
PFS
|
||||
pgd
|
||||
pgdn
|
||||
pgorepro
|
||||
pgort
|
||||
PGU
|
||||
pguid
|
||||
pgup
|
||||
PHANDLE
|
||||
|
@ -1772,7 +1734,6 @@ pid
|
|||
pidl
|
||||
PIDLIST
|
||||
pii
|
||||
pinam
|
||||
pinvoke
|
||||
pipename
|
||||
pipestr
|
||||
|
@ -1887,7 +1848,6 @@ psp
|
|||
PSPCB
|
||||
psr
|
||||
PSTR
|
||||
psuedoconsole
|
||||
psz
|
||||
ptch
|
||||
ptr
|
||||
|
@ -1900,23 +1860,17 @@ PUNICODE
|
|||
pushd
|
||||
putchar
|
||||
putwchar
|
||||
pv
|
||||
PVOID
|
||||
pw
|
||||
pwch
|
||||
PWCHAR
|
||||
PWDDMCONSOLECONTEXT
|
||||
PWORD
|
||||
pws
|
||||
pwsh
|
||||
pwstr
|
||||
pwsz
|
||||
px
|
||||
py
|
||||
pythonw
|
||||
qi
|
||||
QJ
|
||||
qo
|
||||
QOL
|
||||
qos
|
||||
QRSTU
|
||||
qsort
|
||||
queryable
|
||||
|
@ -1987,7 +1941,7 @@ REGSTR
|
|||
reingest
|
||||
Relayout
|
||||
RELBINPATH
|
||||
remoting
|
||||
Remoting
|
||||
renamer
|
||||
renderengine
|
||||
rendersize
|
||||
|
@ -1998,7 +1952,6 @@ Replymessage
|
|||
repositorypath
|
||||
rescap
|
||||
Resequence
|
||||
reserialize
|
||||
RESETCONTENT
|
||||
resheader
|
||||
resizable
|
||||
|
@ -2013,7 +1966,6 @@ rftp
|
|||
rgb
|
||||
rgba
|
||||
rgbi
|
||||
rgch
|
||||
rgci
|
||||
rgfae
|
||||
rgfte
|
||||
|
@ -2027,13 +1979,11 @@ rgui
|
|||
rgw
|
||||
rgwch
|
||||
rhs
|
||||
ri
|
||||
RIGHTALIGN
|
||||
RIGHTBUTTON
|
||||
riid
|
||||
RIPMSG
|
||||
Rl
|
||||
rmdir
|
||||
RIS
|
||||
RMENU
|
||||
roadmap
|
||||
robomac
|
||||
|
@ -2049,7 +1999,6 @@ RTFTo
|
|||
Rtl
|
||||
RTLREADING
|
||||
RTTI
|
||||
ru
|
||||
ruleset
|
||||
runas
|
||||
runasradio
|
||||
|
@ -2065,8 +2014,8 @@ runut
|
|||
runxamlformat
|
||||
rvalue
|
||||
RVERTICAL
|
||||
rxvt
|
||||
RWIN
|
||||
rxvt
|
||||
safearray
|
||||
SAFECAST
|
||||
safemath
|
||||
|
@ -2092,8 +2041,6 @@ scriptload
|
|||
Scrollable
|
||||
scrollback
|
||||
scrollbar
|
||||
Scrolldown
|
||||
Scrolldownpage
|
||||
Scroller
|
||||
SCROLLFORWARD
|
||||
SCROLLINFO
|
||||
|
@ -2101,8 +2048,6 @@ scrolllock
|
|||
scrolloffset
|
||||
SCROLLSCALE
|
||||
SCROLLSCREENBUFFER
|
||||
Scrollup
|
||||
Scrolluppage
|
||||
scursor
|
||||
sddl
|
||||
sdeleted
|
||||
|
@ -2117,6 +2062,7 @@ selectany
|
|||
SELECTEDFONT
|
||||
SELECTSTRING
|
||||
Selfhosters
|
||||
serializers
|
||||
SERVERDLL
|
||||
SETACTIVE
|
||||
SETBUDDYINT
|
||||
|
@ -2201,7 +2147,6 @@ SLIST
|
|||
slmult
|
||||
sln
|
||||
slpit
|
||||
smalllogo
|
||||
SManifest
|
||||
SMARTQUOTE
|
||||
SMTO
|
||||
|
@ -2215,7 +2160,6 @@ SOURCESDIRECTORY
|
|||
SPACEBAR
|
||||
spammy
|
||||
spand
|
||||
splashscreen
|
||||
sprintf
|
||||
sqlproj
|
||||
srand
|
||||
|
@ -2259,7 +2203,6 @@ stgm
|
|||
stl
|
||||
stoi
|
||||
stol
|
||||
storelogo
|
||||
stoul
|
||||
stoutapot
|
||||
strikethrough
|
||||
|
@ -2275,6 +2218,7 @@ STX
|
|||
stylecop
|
||||
SUA
|
||||
subcompartment
|
||||
subfolder
|
||||
subkey
|
||||
SUBLANG
|
||||
sublicensable
|
||||
|
@ -2292,15 +2236,13 @@ swappable
|
|||
SWMR
|
||||
SWP
|
||||
swprintf
|
||||
sx
|
||||
sy
|
||||
SYMED
|
||||
SYNCPAINT
|
||||
sys
|
||||
syscalls
|
||||
SYSCHAR
|
||||
SYSCOMMAND
|
||||
SYSDEADCHAR
|
||||
Sysinternals
|
||||
SYSKEYDOWN
|
||||
SYSKEYUP
|
||||
SYSLINK
|
||||
|
@ -2309,7 +2251,6 @@ sysparams
|
|||
sysparamsext
|
||||
SYSTEMHAND
|
||||
SYSTEMMENU
|
||||
sz
|
||||
tabview
|
||||
TAdd
|
||||
taef
|
||||
|
@ -2362,6 +2303,7 @@ testmddefinition
|
|||
testmode
|
||||
testname
|
||||
testnameprefix
|
||||
testnetv
|
||||
TESTNULL
|
||||
testpass
|
||||
testpasses
|
||||
|
@ -2381,7 +2323,6 @@ textinfo
|
|||
TEXTMETRIC
|
||||
TEXTMETRICW
|
||||
textmode
|
||||
tf
|
||||
TFCAT
|
||||
tfoo
|
||||
TFunction
|
||||
|
@ -2396,7 +2337,6 @@ Timeline
|
|||
titlebar
|
||||
TITLEISLINKNAME
|
||||
TJson
|
||||
tl
|
||||
TLambda
|
||||
TLEN
|
||||
Tlg
|
||||
|
@ -2448,7 +2388,6 @@ tsf
|
|||
TStr
|
||||
TSTRFORMAT
|
||||
TSub
|
||||
tt
|
||||
TTBITMAP
|
||||
ttf
|
||||
TTFONT
|
||||
|
@ -2475,7 +2414,7 @@ uapadmin
|
|||
UAX
|
||||
ubuntu
|
||||
ucd
|
||||
UCD
|
||||
ucd
|
||||
ucdxml
|
||||
uch
|
||||
UCHAR
|
||||
|
@ -2493,7 +2432,6 @@ uielem
|
|||
UIELEMENTENABLEDONLY
|
||||
uint
|
||||
uintptr
|
||||
ul
|
||||
ulcch
|
||||
ulong
|
||||
Unadvise
|
||||
|
@ -2510,22 +2448,21 @@ unicode
|
|||
UNICODESTRING
|
||||
UNICODETEXT
|
||||
UNICRT
|
||||
UNINIT
|
||||
uninit
|
||||
uninitialize
|
||||
uninstall
|
||||
Uniscribe
|
||||
unittest
|
||||
unittesting
|
||||
universaltest
|
||||
Unk
|
||||
unk
|
||||
unknwn
|
||||
unmark
|
||||
UNORM
|
||||
unparseable
|
||||
unpause
|
||||
Unregister
|
||||
Unregistering
|
||||
unte
|
||||
unregistering
|
||||
untests
|
||||
untextured
|
||||
untimes
|
||||
|
@ -2582,7 +2519,6 @@ Vcount
|
|||
vcpkg
|
||||
vcprintf
|
||||
vcproj
|
||||
vcrt
|
||||
vcvarsall
|
||||
vcxitems
|
||||
vcxproj
|
||||
|
@ -2590,7 +2526,6 @@ vec
|
|||
VERCTRL
|
||||
versioning
|
||||
VERTBAR
|
||||
vf
|
||||
VFT
|
||||
vga
|
||||
vgaoem
|
||||
|
@ -2599,7 +2534,6 @@ viewports
|
|||
Virt
|
||||
VIRTTERM
|
||||
Virtualizing
|
||||
vk
|
||||
vkey
|
||||
VKKEYSCAN
|
||||
VMs
|
||||
|
@ -2814,11 +2748,8 @@ WUX
|
|||
WVerify
|
||||
wwaproj
|
||||
WWith
|
||||
wx
|
||||
wxh
|
||||
xa
|
||||
xact
|
||||
xamarin
|
||||
xaml
|
||||
Xamlmeta
|
||||
xargs
|
||||
|
@ -2837,7 +2768,6 @@ xdy
|
|||
XEncoding
|
||||
xes
|
||||
Xes
|
||||
XES
|
||||
xff
|
||||
XFile
|
||||
XFORM
|
||||
|
@ -2848,7 +2778,7 @@ xml
|
|||
xmlns
|
||||
xor
|
||||
xorg
|
||||
XOrg
|
||||
xorg
|
||||
Xpath
|
||||
XPosition
|
||||
XResource
|
||||
|
@ -2860,14 +2790,14 @@ XSubstantial
|
|||
xtended
|
||||
xterm
|
||||
XTest
|
||||
XTPUSHSGR
|
||||
XTPOPSGR
|
||||
XTPUSHSGR
|
||||
xtr
|
||||
xunit
|
||||
xutr
|
||||
xvalue
|
||||
XVIRTUALSCREEN
|
||||
XWalk
|
||||
xy
|
||||
Xzn
|
||||
yact
|
||||
YAML
|
||||
|
@ -2881,17 +2811,8 @@ YPosition
|
|||
YSize
|
||||
YSubstantial
|
||||
YVIRTUALSCREEN
|
||||
Yw
|
||||
YWalk
|
||||
yx
|
||||
YZ
|
||||
Zc
|
||||
ZCmd
|
||||
ZCtrl
|
||||
zd
|
||||
zh
|
||||
ZM
|
||||
zsh
|
||||
zu
|
||||
zxcvbnm
|
||||
zy
|
||||
|
|
4
.github/actions/spelling/expect/web.txt
vendored
4
.github/actions/spelling/expect/web.txt
vendored
|
@ -1,5 +1,4 @@
|
|||
http
|
||||
td
|
||||
www
|
||||
ecma
|
||||
rapidtables
|
||||
|
@ -10,9 +9,8 @@ robertelder
|
|||
kovidgoyal
|
||||
leonerd
|
||||
fixterms
|
||||
uk
|
||||
winui
|
||||
appshellintegration
|
||||
cppreference
|
||||
gfycat
|
||||
what3words
|
||||
Guake
|
||||
|
|
|
@ -22,3 +22,4 @@ Base64::s_(?:En|De)code\(L"[^"]+"
|
|||
VERIFY_ARE_EQUAL\(L"[^"]+"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\+/"
|
||||
std::memory_order_[\w]+
|
||||
D2DERR_SHADER_COMPILE_FAILED
|
||||
|
|
22
.github/actions/spelling/reject.txt
vendored
Normal file
22
.github/actions/spelling/reject.txt
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
^attache$
|
||||
^attacher$
|
||||
^attachers$
|
||||
^spae$
|
||||
^spaebook$
|
||||
^spaecraft$
|
||||
^spaed$
|
||||
^spaedom$
|
||||
^spaeing$
|
||||
^spaeings$
|
||||
^spae-man$
|
||||
^spaeman$
|
||||
^spaer$
|
||||
^Spaerobee$
|
||||
^spaes$
|
||||
^spaewife$
|
||||
^spaewoman$
|
||||
^spaework$
|
||||
^spaewright$
|
||||
^wether$
|
||||
^wethers$
|
||||
^wetherteg$
|
12
.github/workflows/spelling.yml
vendored
12
.github/workflows/spelling.yml
vendored
|
@ -8,8 +8,12 @@ jobs:
|
|||
name: Spell checking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.0.0
|
||||
- name: checkout-merge
|
||||
if: "contains(github.event_name, 'pull_request')"
|
||||
uses: actions/checkout@v2.0.0
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 5
|
||||
- uses: check-spelling/check-spelling@0.0.17-alpha
|
||||
ref: refs/pull/${{github.event.pull_request.number}}/merge
|
||||
- name: checkout
|
||||
if: "!contains(github.event_name, 'pull_request')"
|
||||
uses: actions/checkout@v2.0.0
|
||||
- uses: check-spelling/check-spelling@v0.0.18
|
||||
|
|
75
.vscode/settings.json
vendored
75
.vscode/settings.json
vendored
|
@ -21,7 +21,80 @@
|
|||
"xloctime": "cpp",
|
||||
"multi_span": "cpp",
|
||||
"pointers": "cpp",
|
||||
"vector": "cpp"
|
||||
"vector": "cpp",
|
||||
"bitset": "cpp",
|
||||
"deque": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"list": "cpp",
|
||||
"queue": "cpp",
|
||||
"random": "cpp",
|
||||
"regex": "cpp",
|
||||
"stack": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xtree": "cpp",
|
||||
"xutility": "cpp",
|
||||
"span": "cpp",
|
||||
"string_span": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"cctype": "cpp",
|
||||
"charconv": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"compare": "cpp",
|
||||
"complex": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"exception": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"fstream": "cpp",
|
||||
"functional": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"ios": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"iterator": "cpp",
|
||||
"limits": "cpp",
|
||||
"locale": "cpp",
|
||||
"map": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"ostream": "cpp",
|
||||
"ratio": "cpp",
|
||||
"set": "cpp",
|
||||
"shared_mutex": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"string": "cpp",
|
||||
"system_error": "cpp",
|
||||
"thread": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"xfacet": "cpp",
|
||||
"xiosbase": "cpp",
|
||||
"xlocale": "cpp",
|
||||
"xlocbuf": "cpp",
|
||||
"xlocinfo": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xstddef": "cpp",
|
||||
"xtr1common": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/bin/**": true,
|
||||
|
|
7
.vscode/tasks.json
vendored
7
.vscode/tasks.json
vendored
|
@ -9,7 +9,7 @@
|
|||
"-Command",
|
||||
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
|
||||
"Set-MsBuildDevEnvironment;",
|
||||
"$project = switch(\"${input:buildProjectChoice}\"){OpenConsole{\"Conhost\\Host_EXE\"} Terminal{\"Terminal\\CascadiaPackage\"}};",
|
||||
"$project = switch(\"${input:buildProjectChoice}\"){OpenConsole{\"Conhost\\Host_EXE\"} Terminal{\"Terminal\\CascadiaPackage\"} TermControl{\"Terminal\\TerminalControl\"}};",
|
||||
"$target = switch(\"${input:buildModeChoice}\"){Build{\"\"} Rebuild{\":Rebuild\"} Clean{\":Clean\"}};",
|
||||
"$target = $project + $target;",
|
||||
"msbuild",
|
||||
|
@ -111,10 +111,11 @@
|
|||
"description": "OpenConsole or Terminal?",
|
||||
"options":[
|
||||
"OpenConsole",
|
||||
"Terminal"
|
||||
"Terminal",
|
||||
"TermControl"
|
||||
],
|
||||
"default": "Terminal"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"Microsoft.Net.Component.4.5.TargetingPack",
|
||||
"Microsoft.VisualStudio.Component.DiagnosticTools",
|
||||
"Microsoft.VisualStudio.Component.Debugger.JustInTime",
|
||||
"Microsoft.VisualStudio.Component.Windows10SDK.18362",
|
||||
"Microsoft.VisualStudio.Component.Windows10SDK.19041",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
|
||||
"Microsoft.VisualStudio.Component.VC.CoreIde",
|
||||
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
|
||||
|
|
|
@ -43,7 +43,7 @@ If no existing item describes your issue/feature, great - please file a new issu
|
|||
* Have a question that you don't see answered in docs, videos, etc.? File an issue
|
||||
* Want to know if we're planning on building a particular feature? File an issue
|
||||
* Got a great idea for a new feature? File an issue/request/idea
|
||||
* Don't understand how to do something? File an issue/Community Guidance Request
|
||||
* Don't understand how to do something? File an issue
|
||||
* Found an existing issue that describes yours? Great - upvote and add additional commentary / info / repro-steps / etc.
|
||||
|
||||
When you hit "New Issue", select the type of issue closest to what you want to report/ask/request:
|
||||
|
@ -111,13 +111,13 @@ However, some issues/features will require careful thought & formal design befor
|
|||
|
||||
Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run.
|
||||
|
||||
Specs will be managed in a very similar manner as code contributions so please follow the "Fork, Branch and Create your PR" below.
|
||||
Specs will be managed in a very similar manner as code contributions so please follow the "[Fork, Branch and Create your PR](CONTRIBUTING.md#fork-clone-branch-and-create-your-pr)" section below.
|
||||
|
||||
### Writing / Contributing-to a Spec
|
||||
|
||||
To write/contribute to a spec: fork, branch and commit via PRs, as you would with any code changes.
|
||||
|
||||
Specs are written in markdown, stored under the `\doc\spec` folder and named `[issue id] - [spec description].md`.
|
||||
Specs are written in markdown, stored under the [`\doc\specs`](./doc/specs) folder and named `[issue id] - [spec description].md`.
|
||||
|
||||
👉 **It is important to follow the spec templates and complete the requested information**. The available spec templates will help ensure that specs contain the minimum information & decisions necessary to permit development to begin. In particular, specs require you to confirm that you've already discussed the issue/idea with the team in an issue and that you provide the issue ID for reference.
|
||||
|
||||
|
|
46
NOTICE.md
46
NOTICE.md
|
@ -251,3 +251,49 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
```
|
||||
|
||||
# Microsoft Open Source
|
||||
|
||||
This product also incorporates source code from other Microsoft open source projects, all licensed under the MIT license.
|
||||
|
||||
## `GSL`
|
||||
|
||||
**Source**: [https://github.com/microsoft/GSL](https://github.com/microsoft/GSL)
|
||||
|
||||
## `Microsoft-UI-XAML`
|
||||
|
||||
**Source**: [https://github.com/microsoft/Microsoft-UI-XAML](https://github.com/microsoft/Microsoft-UI-XAML)
|
||||
|
||||
## `VirtualDesktopUtils`
|
||||
|
||||
**Source**: [https://github.com/microsoft/PowerToys](https://github.com/microsoft/PowerToys)
|
||||
|
||||
## `wil`
|
||||
|
||||
**Source**: [https://github.com/microsoft/wil](https://github.com/microsoft/wil)
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
The MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
```
|
||||
|
|
|
@ -175,7 +175,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control.
|
|||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
|
||||
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
|
||||
{1CF55140-EF6A-4736-A403-957E4F7430BB} = {1CF55140-EF6A-4736-A403-957E4F7430BB}
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62} = {48D21369-3D7B-4431-9967-24E81292CF62}
|
||||
{48D21369-3D7B-4431-9967-24E81292CF63} = {48D21369-3D7B-4431-9967-24E81292CF63}
|
||||
{AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} = {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control", "src\cascadia\TerminalControl\dll\TerminalControl.vcxproj", "{CA5CAD1A-F542-4635-A069-7CAEFB930070}"
|
||||
|
@ -393,6 +395,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Control", "src\ca
|
|||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTerminal.UIA.Tests", "src\cascadia\WindowsTerminal_UIATests\WindowsTerminal.UIA.Tests.csproj", "{F19DACD5-0C6E-40DC-B6E4-767A3200542C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
AuditMode|Any CPU = AuditMode|Any CPU
|
||||
|
@ -3254,6 +3258,40 @@ Global
|
|||
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x64.Build.0 = Release|x64
|
||||
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x86.Build.0 = Release|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|Any CPU.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|ARM.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|ARM64.ActiveCfg = Debug|ARM64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|DotNet_x64Test.ActiveCfg = Debug|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|x64.ActiveCfg = Debug|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|x86.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|ARM.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x64.Build.0 = Debug|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|Any CPU.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|ARM.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|DotNet_x64Test.ActiveCfg = Debug|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|x64.ActiveCfg = Debug|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|x86.ActiveCfg = Debug|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|ARM.ActiveCfg = Release|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|DotNet_x64Test.ActiveCfg = Release|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x64.ActiveCfg = Release|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x64.Build.0 = Release|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -3351,6 +3389,7 @@ Global
|
|||
{9921CA0A-320C-4460-8623-3A3196E7F4CB} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{05D9052F-D78F-478F-968A-2DE38A6DB996} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
|
||||
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
![terminal-logos](https://user-images.githubusercontent.com/48369326/115790869-4c852b00-a37c-11eb-97f1-f61972c7800c.png)
|
||||
|
||||
# Welcome to the Windows Terminal, Console and Command-Line repo
|
||||
|
||||
This repository contains the source code for:
|
||||
|
@ -241,7 +243,7 @@ Visual Studio.
|
|||
|
||||
## Documentation
|
||||
|
||||
All project documentation is located at aka.ms/terminal-docs. If you would like
|
||||
All project documentation is located at [aka.ms/terminal-docs](https://aka.ms/terminal-docs). If you would like
|
||||
to contribute to the documentation, please submit a pull request on the [Windows
|
||||
Terminal Documentation repo](https://github.com/MicrosoftDocs/terminal).
|
||||
|
||||
|
@ -276,6 +278,7 @@ If you would like to ask a question that you feel doesn't warrant an issue
|
|||
* Carlos Zamora, Developer: [@cazamor_msft](https://twitter.com/cazamor_msft)
|
||||
* Leon Liang, Developer: [@leonmsft](https://twitter.com/leonmsft)
|
||||
* Pankaj Bhojwani, Developer
|
||||
* Leonard Hecker, Developer: [@LeonardHecker](https://twitter.com/LeonardHecker)
|
||||
|
||||
## Developer Guidance
|
||||
|
||||
|
|
|
@ -29,4 +29,147 @@ function GetQueryTestRunsUri
|
|||
$baseUri = GetAzureDevOpsBaseUri -CollectionUri $CollectionUri -TeamProject $TeamProject
|
||||
$queryUri = "$baseUri/_apis/test/runs?buildUri=$BuildUri$includeRunDetailsParameter&api-version=5.0"
|
||||
return $queryUri
|
||||
}
|
||||
|
||||
function Get-HelixJobTypeFromTestRun
|
||||
{
|
||||
Param ($testRun)
|
||||
|
||||
$testRunSingleResultUri = "$($testRun.url)/results?`$top=1&`$skip=0&api-version=5.1"
|
||||
$singleTestResult = Invoke-RestMethod -Uri $testRunSingleResultUri -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
$count = $singleTestResult.value.Length
|
||||
if($count -eq 0)
|
||||
{
|
||||
# If the count is 0, then results have not yet been reported for this run.
|
||||
# We only care about completed runs with results, so it is ok to just return 'UNKNOWN' for this run.
|
||||
return "UNKNOWN"
|
||||
}
|
||||
else
|
||||
{
|
||||
$info = ConvertFrom-Json $singleTestResult.value.comment
|
||||
$helixJobId = $info.HelixJobId
|
||||
$job = Invoke-RestMethodWithRetries "https://helix.dot.net/api/2019-06-17/jobs/${helixJobId}?access_token=${HelixAccessToken}"
|
||||
return $job.Type
|
||||
}
|
||||
}
|
||||
|
||||
function Append-HelixAccessTokenToUrl
|
||||
{
|
||||
Param ([string]$url, [string]$token)
|
||||
if($url.Contains("?"))
|
||||
{
|
||||
$url = "$($url)&access_token=$($token)"
|
||||
}
|
||||
else
|
||||
{
|
||||
$url = "$($url)?access_token=$($token)"
|
||||
}
|
||||
return $url
|
||||
}
|
||||
|
||||
|
||||
# The Helix Rest api is sometimes unreliable. So we call these apis with retry logic.
|
||||
# Note: The Azure DevOps apis are stable and do not need to be called with this retry logic.
|
||||
$helixApiRetries = 0
|
||||
$helixApiRetriesMax = 10
|
||||
|
||||
function Download-StringWithRetries
|
||||
{
|
||||
Param ([string]$fileName, [string]$url)
|
||||
|
||||
$result = ""
|
||||
$done = $false
|
||||
|
||||
while(!($done))
|
||||
{
|
||||
try
|
||||
{
|
||||
Write-Host "Downloading $fileName"
|
||||
$result = (New-Object System.Net.WebClient).DownloadString($url)
|
||||
$done = $true
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Host "Failed to download $fileName $($PSItem.Exception)"
|
||||
|
||||
$helixApiRetries = $helixApiRetries + 1
|
||||
if($helixApiRetries -lt $helixApiRetriesMax)
|
||||
{
|
||||
Write-Host "Sleep and retry download of $fileName"
|
||||
Start-Sleep 60
|
||||
}
|
||||
else
|
||||
{
|
||||
throw "Failed to download $fileName"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result
|
||||
}
|
||||
|
||||
function Invoke-RestMethodWithRetries
|
||||
{
|
||||
Param ([string]$url,$Headers)
|
||||
|
||||
$result = @()
|
||||
$done = $false
|
||||
|
||||
while(!($done))
|
||||
{
|
||||
try
|
||||
{
|
||||
$result = Invoke-RestMethod -Uri $url -Method Get -Headers $Headers
|
||||
$done = $true
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Host "Failed to invoke Rest method $($PSItem.Exception)"
|
||||
|
||||
$helixApiRetries = $helixApiRetries + 1
|
||||
if($helixApiRetries -lt $helixApiRetriesMax)
|
||||
{
|
||||
Write-Host "Sleep and retry invoke"
|
||||
Start-Sleep 60
|
||||
}
|
||||
else
|
||||
{
|
||||
throw "Failed to invoke Rest method"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result
|
||||
}
|
||||
|
||||
function Download-FileWithRetries
|
||||
{
|
||||
Param ([string]$fileurl, [string]$destination)
|
||||
|
||||
$done = $false
|
||||
|
||||
while(!($done))
|
||||
{
|
||||
try
|
||||
{
|
||||
Write-Host "Downloading $destination"
|
||||
$webClient.DownloadFile($fileurl, $destination)
|
||||
$done = $true
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Host "Failed to download $destination $($PSItem.Exception)"
|
||||
|
||||
$helixApiRetries = $helixApiRetries + 1
|
||||
if($helixApiRetries -lt $helixApiRetriesMax)
|
||||
{
|
||||
Write-Host "Sleep and retry download of $destination"
|
||||
Start-Sleep 60
|
||||
}
|
||||
else
|
||||
{
|
||||
throw "Failed to download $destination"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,10 +20,12 @@ 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\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\*" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\CoreClr\*" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\NetFx4.5\*" $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\"
|
||||
New-Item -ItemType Directory -Force -Path "$payloadDir\content\"
|
||||
Copy-Item "$nugetPackagesDir\Microsoft.Internal.Windows.Terminal.TestContent.1.0.1\content\*" "$payloadDir\content\"
|
||||
|
||||
function Copy-If-Exists
|
||||
{
|
||||
|
@ -52,3 +54,13 @@ Copy-Item "build\helix\HelixTestHelpers.cs" "$payloadDir"
|
|||
Copy-Item "build\helix\runtests.cmd" $payloadDir
|
||||
Copy-Item "build\helix\InstallTestAppDependencies.ps1" "$payloadDir"
|
||||
Copy-Item "build\Helix\EnsureMachineState.ps1" "$payloadDir"
|
||||
|
||||
# Copy the APPX package from the 'drop' artifact dir
|
||||
Copy-Item "$repoDirectory\Artifacts\$ArtifactName\appx\CascadiaPackage_0.0.1.0_$Platform.msix" $payloadDir\CascadiaPackage.zip
|
||||
|
||||
# Rename it to extension of ZIP because Expand-Archive is real sassy on the build machines
|
||||
# and refuses to unzip it because of its file extension while on a desktop, it just
|
||||
# does the job without complaining.
|
||||
|
||||
# Extract the APPX package
|
||||
Expand-Archive -LiteralPath $payloadDir\CascadiaPackage.zip -DestinationPath $payloadDir\appx
|
||||
|
|
|
@ -9,11 +9,6 @@ Param(
|
|||
|
||||
$helixLinkFile = "$OutputFolder\LinksToHelixTestFiles.html"
|
||||
|
||||
$accessTokenParam = ""
|
||||
if($HelixAccessToken)
|
||||
{
|
||||
$accessTokenParam = "?access_token=$HelixAccessToken"
|
||||
}
|
||||
|
||||
function Generate-File-Links
|
||||
{
|
||||
|
@ -45,66 +40,69 @@ $azureDevOpsRestApiHeaders = @{
|
|||
$queryUri = GetQueryTestRunsUri -CollectionUri $CollectionUri -TeamProject $TeamProject -BuildUri $BuildUri -IncludeRunDetails
|
||||
Write-Host "queryUri = $queryUri"
|
||||
|
||||
$testRuns = Invoke-RestMethod -Uri $queryUri -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
[System.Collections.Generic.List[string]]$workItems = @()
|
||||
|
||||
foreach ($testRun in $testRuns.value)
|
||||
{
|
||||
$testResults = Invoke-RestMethod -Uri "$($testRun.url)/results?api-version=5.0" -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
Write-Host "testRunUri = $testRun.url"
|
||||
$testResults = Invoke-RestMethodWithRetries "$($testRun.url)/results?api-version=5.0" -Headers $azureDevOpsRestApiHeaders
|
||||
$isTestRunNameShown = $false
|
||||
|
||||
foreach ($testResult in $testResults.value)
|
||||
{
|
||||
if ("comment" -in $testResult)
|
||||
$info = ConvertFrom-Json $testResult.comment
|
||||
$helixJobId = $info.HelixJobId
|
||||
$helixWorkItemName = $info.HelixWorkItemName
|
||||
|
||||
$workItem = "$helixJobId-$helixWorkItemName"
|
||||
|
||||
Write-Host "Helix Work Item = $workItem"
|
||||
|
||||
if (-not $workItems.Contains($workItem))
|
||||
{
|
||||
$info = ConvertFrom-Json $testResult.comment
|
||||
$helixJobId = $info.HelixJobId
|
||||
$helixWorkItemName = $info.HelixWorkItemName
|
||||
$workItems.Add($workItem)
|
||||
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files$accessTokenParam"
|
||||
$files = Invoke-RestMethodWithRetries $filesQueryUri
|
||||
|
||||
$workItem = "$helixJobId-$helixWorkItemName"
|
||||
|
||||
if (-not $workItems.Contains($workItem))
|
||||
$screenShots = $files | where { $_.Name.EndsWith(".jpg") }
|
||||
$dumps = $files | where { $_.Name.EndsWith(".dmp") }
|
||||
$pgcFiles = $files | where { $_.Name.EndsWith(".pgc") }
|
||||
if ($screenShots.Count + $dumps.Count + $pgcFiles.Count -gt 0)
|
||||
{
|
||||
$workItems.Add($workItem)
|
||||
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files$accessTokenParam"
|
||||
$files = Invoke-RestMethod -Uri $filesQueryUri -Method Get
|
||||
|
||||
$screenShots = $files | where { $_.Name.EndsWith(".jpg") }
|
||||
$dumps = $files | where { $_.Name.EndsWith(".dmp") }
|
||||
$pgcFiles = $files | where { $_.Name.EndsWith(".pgc") }
|
||||
if ($screenShots.Count + $dumps.Count + $pgcFiles.Count -gt 0)
|
||||
if(-Not $isTestRunNameShown)
|
||||
{
|
||||
if(-Not $isTestRunNameShown)
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<h2>$($testRun.name)</h2>"
|
||||
$isTestRunNameShown = $true
|
||||
}
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<h3>$helixWorkItemName</h3>"
|
||||
Generate-File-Links $screenShots "Screenshots"
|
||||
Generate-File-Links $dumps "CrashDumps"
|
||||
Generate-File-Links $pgcFiles "PGC files"
|
||||
$misc = $files | where { ($screenShots -NotContains $_) -And ($dumps -NotContains $_) -And ($visualTreeVerificationFiles -NotContains $_) -And ($pgcFiles -NotContains $_) }
|
||||
Generate-File-Links $misc "Misc"
|
||||
|
||||
foreach($pgcFile in $pgcFiles)
|
||||
{
|
||||
$flavorPath = $testResult.automatedTestName.Split('.')[0]
|
||||
$archPath = $testResult.automatedTestName.Split('.')[1]
|
||||
$fileName = $pgcFile.Name
|
||||
$fullPath = "$OutputFolder\PGO\$flavorPath\$archPath"
|
||||
$destination = "$fullPath\$fileName"
|
||||
|
||||
Write-Host "Copying $($pgcFile.Name) to $destination"
|
||||
|
||||
if (-Not (Test-Path $fullPath))
|
||||
{
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<h2>$($testRun.name)</h2>"
|
||||
$isTestRunNameShown = $true
|
||||
New-Item $fullPath -ItemType Directory
|
||||
}
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<h3>$helixWorkItemName</h3>"
|
||||
Generate-File-Links $screenShots "Screenshots"
|
||||
Generate-File-Links $dumps "CrashDumps"
|
||||
Generate-File-Links $pgcFiles "PGC files"
|
||||
$misc = $files | where { ($screenShots -NotContains $_) -And ($dumps -NotContains $_) -And ($visualTreeVerificationFiles -NotContains $_) -And ($pgcFiles -NotContains $_) }
|
||||
Generate-File-Links $misc "Misc"
|
||||
|
||||
foreach($pgcFile in $pgcFiles)
|
||||
{
|
||||
$flavorPath = $pgcFile.Name.Split('.')[0]
|
||||
$archPath = $pgcFile.Name.Split('.')[1]
|
||||
$fileName = $pgcFile.Name.Remove(0, $flavorPath.length + $archPath.length + 2)
|
||||
$fullPath = "$OutputFolder\PGO\$flavorPath\$archPath"
|
||||
$destination = "$fullPath\$fileName"
|
||||
$link = $pgcFile.Link
|
||||
|
||||
Write-Host "Copying $($pgcFile.Name) to $destination"
|
||||
Write-Host "Downloading $link to $destination"
|
||||
|
||||
if (-Not (Test-Path $fullPath))
|
||||
{
|
||||
New-Item $fullPath -ItemType Directory
|
||||
}
|
||||
|
||||
$link = "$($pgcFile.Link)$accessTokenParam"
|
||||
$webClient.DownloadFile($link, $destination)
|
||||
}
|
||||
Download-FileWithRetries $link $destination
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<!-- These .proj files are generated by the build machine prior to running tests via GenerateTestProjFile.ps1. -->
|
||||
<Import Project="$(ProjFilesPath)\RunTestsInHelix-TerminalAppLocalTests.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
|
||||
<Import Project="$(ProjFilesPath)\RunTestsInHelix-HostTestsUIA.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
|
||||
<Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-TerminalAppLocalTests.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
|
||||
<Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-SettingsModelLocalTests.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
|
||||
<Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-HostTestsUIA.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
|
||||
<Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-WindowsTerminalUIATests.proj" Condition=" '$(TestSuite)'=='PgoInstrumentationSuite' " />
|
||||
</Project>
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.Windows.Terminal.TestContent" version="1.0.1" />
|
||||
<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" />
|
||||
|
|
|
@ -28,7 +28,7 @@ echo %TIME%
|
|||
powershell -ExecutionPolicy Bypass .\InstallTestAppDependencies.ps1
|
||||
echo %TIME%
|
||||
|
||||
set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Conhost.UIA.Tests.dll
|
||||
set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Conhost.UIA.Tests.dll WindowsTerminal.UIA.Tests.dll
|
||||
set testBinaries=
|
||||
for %%B in (%testBinaryCandidates%) do (
|
||||
if exist %%B (
|
||||
|
@ -46,7 +46,6 @@ move te.wtl te_original.wtl
|
|||
|
||||
copy /y te_original.wtl %HELIX_WORKITEM_UPLOAD_ROOT%
|
||||
copy /y WexLogFileOutput\*.jpg %HELIX_WORKITEM_UPLOAD_ROOT%
|
||||
for /f "tokens=* delims=" %%a in ('dir /b *.pgc') do ren "%%a" "%testnameprefix%.%%~na.pgc"
|
||||
copy /y *.pgc %HELIX_WORKITEM_UPLOAD_ROOT%
|
||||
|
||||
set FailedTestQuery=
|
||||
|
|
27
build/pipelines/pgo.yml
Normal file
27
build/pipelines/pgo.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
trigger: none
|
||||
pr: none
|
||||
|
||||
variables:
|
||||
- name: runCodesignValidationInjectionBG
|
||||
value: false
|
||||
|
||||
# 0.0.yyMM.dd##
|
||||
# 0.0.1904.0900
|
||||
name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
|
||||
|
||||
stages:
|
||||
- stage: Build_x64
|
||||
displayName: Build x64
|
||||
dependsOn: []
|
||||
condition: succeeded()
|
||||
jobs:
|
||||
- template: ./templates/build-console-pgo.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
- stage: Publish_PGO_Databases
|
||||
displayName: Publish PGO databases
|
||||
dependsOn: ['Build_x64']
|
||||
jobs:
|
||||
- template: ./templates/pgo-build-and-publish-nuget-job.yml
|
||||
parameters:
|
||||
pgoArtifact: 'PGO'
|
|
@ -20,12 +20,20 @@ jobs:
|
|||
parameters:
|
||||
additionalBuildArguments: ${{ parameters.additionalBuildArguments }}
|
||||
|
||||
# It appears that the Component Governance build task that gets automatically injected stopped working
|
||||
# when we renamed our main branch.
|
||||
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
|
||||
displayName: 'Component Detection'
|
||||
condition: and(succeededOrFailed(), not(eq(variables['Build.Reason'], 'PullRequest')))
|
||||
|
||||
- template: helix-runtests-job.yml
|
||||
parameters:
|
||||
name: 'RunTestsInHelix'
|
||||
dependsOn: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
condition: and(succeeded(), and(eq('${{ parameters.platform }}', 'x64'), not(eq(variables['Build.Reason'], 'PullRequest'))))
|
||||
testSuite: 'DevTestSuite'
|
||||
platform: ${{ parameters.platform }}
|
||||
configuration: ${{ parameters.configuration }}
|
||||
rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
|
||||
|
||||
- template: helix-processtestresults-job.yml
|
||||
|
|
|
@ -9,6 +9,7 @@ jobs:
|
|||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
PGOBuildMode: 'Optimize'
|
||||
|
||||
pool:
|
||||
name: Package ES Lab E
|
||||
|
|
51
build/pipelines/templates/build-console-pgo.yml
Normal file
51
build/pipelines/templates/build-console-pgo.yml
Normal file
|
@ -0,0 +1,51 @@
|
|||
parameters:
|
||||
configuration: 'Release'
|
||||
platform: ''
|
||||
additionalBuildArguments: ''
|
||||
minimumExpectedTestsExecutedCount: 1 # Sanity check for minimum expected tests to be reported
|
||||
rerunPassesRequiredToAvoidFailure: 0
|
||||
|
||||
jobs:
|
||||
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }}
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
PGOBuildMode: 'Instrument'
|
||||
pool: "windevbuildagents"
|
||||
# The public pool is also an option!
|
||||
# pool: { vmImage: windows-2019 }
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
parameters:
|
||||
additionalBuildArguments: ${{ parameters.additionalBuildArguments }}
|
||||
|
||||
- template: helix-runtests-job.yml
|
||||
parameters:
|
||||
name: 'RunTestsInHelix'
|
||||
dependsOn: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
condition: succeeded()
|
||||
testSuite: 'PgoInstrumentationSuite'
|
||||
taefQuery: '@IsPgo=true'
|
||||
configuration: ${{ parameters.configuration }}
|
||||
platform: ${{ parameters.platform }}
|
||||
rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
|
||||
|
||||
- template: helix-processtestresults-job.yml
|
||||
parameters:
|
||||
name: 'ProcessTestResults'
|
||||
pgoArtifact: 'PGO'
|
||||
dependsOn:
|
||||
- RunTestsInHelix
|
||||
condition: succeededOrFailed()
|
||||
rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
|
||||
minimumExpectedTestsExecutedCount: ${{ parameters.minimumExpectedTestsExecutedCount }}
|
||||
|
||||
- template: pgo-merge-pgd-job.yml
|
||||
parameters:
|
||||
name: 'MergePGD'
|
||||
dependsOn:
|
||||
- ProcessTestResults
|
||||
pgoArtifact: 'PGO'
|
||||
platform: ${{ parameters.platform }}
|
|
@ -22,7 +22,7 @@ steps:
|
|||
configPath: NuGet.config
|
||||
restoreSolution: OpenConsole.sln
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\packages'
|
||||
|
||||
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: Restore NuGet packages for extraneous build actions
|
||||
inputs:
|
||||
|
@ -32,6 +32,29 @@ steps:
|
|||
restoreSolution: build/packages.config
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\packages'
|
||||
|
||||
# The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves.
|
||||
- script: |
|
||||
"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt
|
||||
set /p _VSINSTALLDIR15=<%TEMP%\vsinstalldir.txt
|
||||
del %TEMP%\vsinstalldir.txt
|
||||
call "%_VSINSTALLDIR15%\Common7\Tools\VsDevCmd.bat"
|
||||
echo VCToolsInstallDir = %VCToolsInstallDir%
|
||||
echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
|
||||
displayName: 'Retrieve VC tools directory'
|
||||
|
||||
- task: CmdLine@1
|
||||
displayName: 'Display build machine environment variables'
|
||||
inputs:
|
||||
filename: 'set'
|
||||
|
||||
- task: powershell@2
|
||||
displayName: 'Restore PGO database'
|
||||
condition: eq(variables['PGOBuildMode'], 'Optimize')
|
||||
inputs:
|
||||
targetType: filePath
|
||||
workingDirectory: $(Build.SourcesDirectory)\tools\PGODatabase
|
||||
filePath: $(Build.SourcesDirectory)\tools\PGODatabase\restore-pgodb.ps1
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
|
@ -45,6 +68,9 @@ steps:
|
|||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Check MSIX for common regressions'
|
||||
# PGO runtime needs its own CRT and it's in the package for convenience.
|
||||
# That will make this script mad so skip since we're not shipping the PGO Instrumentation one anyway.
|
||||
condition: ne(variables['PGOBuildMode'], 'Instrument')
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
|
@ -53,6 +79,7 @@ steps:
|
|||
|
||||
- task: powershell@2
|
||||
displayName: 'Source Index PDBs'
|
||||
condition: ne(variables['PGOBuildMode'], 'Instrument')
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\scripts\Index-Pdbs.ps1
|
||||
|
@ -68,13 +95,25 @@ steps:
|
|||
If ($Arch -Eq "x86") { $Arch = "Win32" }
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Validate binaries are optimized'
|
||||
condition: eq(variables['pgoBuildMode'], 'Optimize')
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
$Binaries = 'OpenConsole.exe', 'WindowsTerminal.exe', 'TerminalApp.dll', 'TerminalConnection.dll', 'Microsoft.Terminal.Control.dll', 'Microsoft.Terminal.Remoting.dll', 'Microsoft.Terminal.Settings.Editor.dll', 'Microsoft.Terminal.Settings.Model.dll'
|
||||
foreach ($BinFile in $Binaries)
|
||||
{
|
||||
& "$(Build.SourcesDirectory)\tools\PGODatabase\verify-pgo.ps1" "$(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/$BinFile"
|
||||
}
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Run Unit Tests'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*unit.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}'
|
||||
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
|
||||
condition: and(and(succeeded(), ne(variables['PGOBuildMode'], 'Instrument')), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Run Feature Tests (x64 only)'
|
||||
|
@ -82,7 +121,7 @@ steps:
|
|||
targetType: filePath
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}'
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
condition: and(and(succeeded(), ne(variables['PGOBuildMode'], 'Instrument')), eq(variables['BuildPlatform'], 'x64'))
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Convert Test Logs from WTL to xUnit format'
|
||||
|
@ -90,13 +129,14 @@ steps:
|
|||
targetType: filePath
|
||||
filePath: build\Helix\ConvertWttLogToXUnit.ps1
|
||||
arguments: -WttInputPath '${{ parameters.testLogPath }}' -WttSingleRerunInputPath 'unused.wtl' -WttMultipleRerunInputPath 'unused2.wtl' -XUnitOutputPath 'onBuildMachineResults.xml' -TestNamePrefix '$(BuildConfiguration).$(BuildPlatform)'
|
||||
condition: or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86'))
|
||||
condition: and(ne(variables['PGOBuildMode'], 'Instrument'),or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Upload converted test logs'
|
||||
condition: ne(variables['PGOBuildMode'], 'Instrument')
|
||||
inputs:
|
||||
testResultsFormat: 'xUnit' # Options: JUnit, NUnit, VSTest, xUnit, cTest
|
||||
testResultsFiles: '**/onBuildMachineResults.xml'
|
||||
testResultsFiles: '**/onBuildMachineResults.xml'
|
||||
#searchFolder: '$(System.DefaultWorkingDirectory)' # Optional
|
||||
#mergeTestResults: false # Optional
|
||||
#failTaskOnFailedTests: false # Optional
|
||||
|
@ -127,24 +167,47 @@ steps:
|
|||
TargetFolder: '$(Build.ArtifactStagingDirectory)/appx'
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
|
||||
condition: succeeded()
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy outputs needed for test runs to Artifacts'
|
||||
inputs:
|
||||
Contents: |
|
||||
$(Build.SourcesDirectory)/bin/$(BuildPlatform)/$(BuildConfiguration)/*.exe
|
||||
$(Build.SourcesDirectory)/bin/$(BuildPlatform)/$(BuildConfiguration)/*.dll
|
||||
$(Build.SourcesDirectory)/bin/$(BuildPlatform)/$(BuildConfiguration)/*.xml
|
||||
$(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.exe
|
||||
$(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.dll
|
||||
$(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.xml
|
||||
**/Microsoft.VCLibs.*.appx
|
||||
**/TestHostApp/*
|
||||
**/TestHostApp/*.exe
|
||||
**/TestHostApp/*.dll
|
||||
**/TestHostApp/*.xml
|
||||
!**/*.pdb
|
||||
!**/*.ipdb
|
||||
!**/*.obj
|
||||
!**/*.pch
|
||||
TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/test'
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
condition: and(and(succeeded(), eq(variables['BuildPlatform'], 'x64')), ne(variables['Build.Reason'], 'PullRequest'))
|
||||
condition: succeeded()
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish All Build Artifacts'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'drop'
|
||||
ArtifactName: 'drop'
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy PGO databases needed for PGO instrumentation run'
|
||||
inputs:
|
||||
Contents: |
|
||||
**/*.pgd
|
||||
TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/PGO/$(BuildPlatform)'
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
condition: and(succeeded(), eq(variables['PGOBuildMode'], 'Instrument'))
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish All PGO Artifacts'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/PGO'
|
||||
ArtifactName: 'PGO'
|
||||
condition: and(succeeded(), eq(variables['PGOBuildMode'], 'Instrument'))
|
||||
|
|
|
@ -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\Microsoft.Taef.10.58.210305002\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'
|
||||
arguments: -TestFile '${{ parameters.testFilePath }}' -OutputProjFile '$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\$(BuildPlatform)\${{ parameters.outputProjFileName }}' -JobTestSuiteName '${{ parameters.testSuite }}' -TaefPath '$(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.58.210305002\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'
|
|
@ -10,19 +10,11 @@ parameters:
|
|||
maxParallel: 4
|
||||
rerunPassesRequiredToAvoidFailure: 5
|
||||
taefQuery: ''
|
||||
configuration: ''
|
||||
platform: ''
|
||||
# if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline:
|
||||
useBuildOutputFromPipeline: $(System.DefinitionId)
|
||||
matrix:
|
||||
# Release_x86:
|
||||
# buildPlatform: 'x86'
|
||||
# buildConfiguration: 'release'
|
||||
# openHelixTargetQueues: 'windows.10.amd64.client19h1.open.xaml'
|
||||
# closedHelixTargetQueues: 'windows.10.amd64.client19h1.xaml'
|
||||
Release_x64:
|
||||
buildPlatform: 'x64'
|
||||
buildConfiguration: 'release'
|
||||
openHelixTargetQueues: 'windows.10.amd64.client19h1.open.xaml'
|
||||
closedHelixTargetQueues: 'windows.10.amd64.client19h1.xaml'
|
||||
openHelixTargetQueues: 'windows.10.amd64.client19h1.open.xaml'
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
|
@ -33,8 +25,10 @@ jobs:
|
|||
timeoutInMinutes: 120
|
||||
strategy:
|
||||
maxParallel: ${{ parameters.maxParallel }}
|
||||
matrix: ${{ parameters.matrix }}
|
||||
variables:
|
||||
buildConfiguration: ${{ parameters.configuration }}
|
||||
buildPlatform: ${{ parameters.platform }}
|
||||
openHelixTargetQueues: ${{ parameters.openHelixTargetQueues }}
|
||||
artifactsDir: $(Build.SourcesDirectory)\Artifacts
|
||||
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,9 +91,17 @@ jobs:
|
|||
filename: 'dir'
|
||||
arguments: '/s $(Build.SourcesDirectory)\HelixPayload'
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Make artifact directories'
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
New-Item -ItemType Directory -Force -Path "$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\"
|
||||
New-Item -ItemType Directory -Force -Path "$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\$(BuildPlatform)\"
|
||||
|
||||
- template: helix-createprojfile-steps.yml
|
||||
parameters:
|
||||
condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite'))
|
||||
condition: and(succeeded(),eq('${{ parameters.testSuite }}','DevTestSuite'))
|
||||
testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\TerminalApp.LocalTests.dll'
|
||||
outputProjFileName: 'RunTestsInHelix-TerminalAppLocalTests.proj'
|
||||
testSuite: '${{ parameters.testSuite }}'
|
||||
|
@ -107,7 +109,7 @@ jobs:
|
|||
|
||||
- template: helix-createprojfile-steps.yml
|
||||
parameters:
|
||||
condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite'))
|
||||
condition: and(succeeded(),eq('${{ parameters.testSuite }}','DevTestSuite'))
|
||||
testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\SettingsModel.LocalTests.dll'
|
||||
outputProjFileName: 'RunTestsInHelix-SettingsModelLocalTests.proj'
|
||||
testSuite: '${{ parameters.testSuite }}'
|
||||
|
@ -116,12 +118,20 @@ jobs:
|
|||
|
||||
- template: helix-createprojfile-steps.yml
|
||||
parameters:
|
||||
condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite'))
|
||||
condition: and(succeeded(),eq('${{ parameters.testSuite }}','DevTestSuite'))
|
||||
testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\Conhost.UIA.Tests.dll'
|
||||
outputProjFileName: 'RunTestsInHelix-HostTestsUIA.proj'
|
||||
testSuite: '${{ parameters.testSuite }}'
|
||||
taefQuery: ${{ parameters.taefQuery }}
|
||||
|
||||
- template: helix-createprojfile-steps.yml
|
||||
parameters:
|
||||
condition: and(succeeded(),or(eq('${{ parameters.testSuite }}','PgoInstrumentationSuite'),eq('${{ parameters.testSuite }}','DevTestSuite')))
|
||||
testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\WindowsTerminal.UIA.Tests.dll'
|
||||
outputProjFileName: 'RunTestsInHelix-WindowsTerminalUIATests.proj'
|
||||
testSuite: '${{ parameters.testSuite }}'
|
||||
taefQuery: ${{ parameters.taefQuery }}
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish generated .proj files'
|
||||
inputs:
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# From our friends at MUX: https://github.com/microsoft/microsoft-ui-xaml/blob/main/build/AzurePipelinesTemplates/MUX-BuildAndPublishPGONuGet-Job.yml
|
||||
|
||||
parameters:
|
||||
dependsOn: ''
|
||||
pgoArtifact: PGO
|
||||
|
||||
jobs:
|
||||
- job: BuildAndPublishPGONuGet
|
||||
dependsOn: ${{ parameters.dependsOn }}
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
variables:
|
||||
artifactsPath: $(Build.SourcesDirectory)\Artifacts
|
||||
pgoToolsPath: $(Build.SourcesDirectory)\tools\PGODatabase
|
||||
nuspecPath: $(pgoToolsPath)\NuSpecs
|
||||
nuspecFilename: PGO.nuspec
|
||||
|
||||
steps:
|
||||
- task: DownloadBuildArtifacts@0
|
||||
inputs:
|
||||
artifactName: ${{ parameters.pgoArtifact }}
|
||||
downloadPath: $(artifactsPath)
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: 'Use NuGet 5.2.0'
|
||||
inputs:
|
||||
versionSpec: 5.2.0
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy pgd files to NuGet build directory'
|
||||
inputs:
|
||||
sourceFolder: $(artifactsPath)\${{ parameters.pgoArtifact }}
|
||||
contents: '**\*.pgd'
|
||||
targetFolder: $(nuspecPath)\tools
|
||||
|
||||
- task: powershell@2
|
||||
displayName: 'Generate NuSpec file'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: $(pgoToolsPath)\generate-nuspec.ps1
|
||||
workingDirectory: $(pgoToolsPath)
|
||||
arguments: $(nuspecPath)\$(nuspecFilename).template $(nuspecPath)\$(nuspecFilename)
|
||||
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: 'NuGet pack'
|
||||
inputs:
|
||||
command: pack
|
||||
packagesToPack: '$(nuspecPath)\$(nuspecFilename)'
|
||||
basePath: '$(nuspecPath)'
|
||||
packDestination: '$(Build.ArtifactStagingDirectory)'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
pathToPublish: $(Build.ArtifactStagingDirectory)
|
||||
artifactName: ${{ parameters.pgoArtifact }}
|
||||
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: 'NuGet push'
|
||||
inputs:
|
||||
command: push
|
||||
publishVstsFeed: Terminal/TerminalDependencies
|
||||
packagesToPush: $(Build.ArtifactStagingDirectory)/*.nupkg
|
90
build/pipelines/templates/pgo-merge-pgd-job.yml
Normal file
90
build/pipelines/templates/pgo-merge-pgd-job.yml
Normal file
|
@ -0,0 +1,90 @@
|
|||
parameters:
|
||||
dependsOn: ''
|
||||
pgoArtifact: PGO
|
||||
platform: ''
|
||||
|
||||
jobs:
|
||||
- job: MergePGD
|
||||
dependsOn: ${{ parameters.dependsOn }}
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
variables:
|
||||
artifactsPath: $(Build.SourcesDirectory)\Artifacts
|
||||
pgoArtifactsPath: $(artifactsPath)\${{ parameters.pgoArtifact }}
|
||||
buildPlatform: ${{ parameters.platform }}
|
||||
|
||||
steps:
|
||||
# The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves.
|
||||
- script: |
|
||||
"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt
|
||||
set /p _VSINSTALLDIR15=<%TEMP%\vsinstalldir.txt
|
||||
del %TEMP%\vsinstalldir.txt
|
||||
call "%_VSINSTALLDIR15%\Common7\Tools\VsDevCmd.bat"
|
||||
echo VCToolsInstallDir = %VCToolsInstallDir%
|
||||
echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
|
||||
displayName: 'Retrieve VC tools directory'
|
||||
|
||||
- task: DownloadBuildArtifacts@0
|
||||
inputs:
|
||||
artifactName: ${{ parameters.pgoArtifact }}
|
||||
downloadPath: $(artifactsPath)
|
||||
|
||||
- script: |
|
||||
cd $(buildPlatform)
|
||||
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge WindowsTerminal*.pgc WindowsTerminal.pgd
|
||||
displayName: 'Merge Terminal pgc files into pgd'
|
||||
workingDirectory: $(pgoArtifactsPath)
|
||||
|
||||
- script: |
|
||||
cd $(buildPlatform)
|
||||
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge OpenConsole*.pgc OpenConsole.pgd
|
||||
displayName: 'Merge OpenConsole pgc files into pgd'
|
||||
workingDirectory: $(pgoArtifactsPath)
|
||||
|
||||
- script: |
|
||||
cd $(buildPlatform)
|
||||
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge Microsoft.Terminal.Control*.pgc Microsoft.Terminal.Control.pgd
|
||||
displayName: 'Merge Microsoft.Terminal.Control pgc files into pgd'
|
||||
workingDirectory: $(pgoArtifactsPath)
|
||||
|
||||
- script: |
|
||||
cd $(buildPlatform)
|
||||
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge Microsoft.Terminal.Remoting*.pgc Microsoft.Terminal.Remoting.pgd
|
||||
displayName: 'Merge Microsoft.Terminal.Remoting pgc files into pgd'
|
||||
workingDirectory: $(pgoArtifactsPath)
|
||||
|
||||
- script: |
|
||||
cd $(buildPlatform)
|
||||
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge Microsoft.Terminal.Settings.Editor*.pgc Microsoft.Terminal.Settings.Editor.pgd
|
||||
displayName: 'Merge Microsoft.Terminal.Settings.Editor pgc files into pgd'
|
||||
workingDirectory: $(pgoArtifactsPath)
|
||||
|
||||
- script: |
|
||||
cd $(buildPlatform)
|
||||
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge Microsoft.Terminal.Settings.Model*.pgc Microsoft.Terminal.Settings.Model.pgd
|
||||
displayName: 'Merge Microsoft.Terminal.Settings.Model pgc files into pgd'
|
||||
workingDirectory: $(pgoArtifactsPath)
|
||||
|
||||
- script: |
|
||||
cd $(buildPlatform)
|
||||
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge TerminalApp*.pgc TerminalApp.pgd
|
||||
displayName: 'Merge TerminalApp pgc files into pgd'
|
||||
workingDirectory: $(pgoArtifactsPath)
|
||||
|
||||
- script: |
|
||||
cd $(buildPlatform)
|
||||
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge TerminalConnection*.pgc TerminalConnection.pgd
|
||||
displayName: 'Merge TerminalConnection pgc files into pgd'
|
||||
workingDirectory: $(pgoArtifactsPath)
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy merged pgd to artifact staging'
|
||||
inputs:
|
||||
sourceFolder: $(pgoArtifactsPath)
|
||||
contents: '**\$(buildPlatform)\*.pgd'
|
||||
targetFolder: $(Build.ArtifactStagingDirectory)
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
pathToPublish: $(Build.ArtifactStagingDirectory)
|
||||
artifactName: ${{ parameters.pgoArtifact }}
|
|
@ -22,7 +22,7 @@ Param(
|
|||
[Parameter(HelpMessage="Path to makeappx.exe")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86\MakeAppx.exe"
|
||||
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86\MakeAppx.exe"
|
||||
)
|
||||
|
||||
If ($null -Eq (Get-Item $MakeAppxPath -EA:SilentlyContinue)) {
|
||||
|
|
|
@ -19,7 +19,7 @@ function Invoke-CheckBadCodeFormatting() {
|
|||
}
|
||||
|
||||
# Manually check the formatting of our .xaml files, without touching them.
|
||||
Verify-XamlFormat
|
||||
Test-XamlFormat
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ Param(
|
|||
[Parameter(HelpMessage="Path to Windows Kit")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0"
|
||||
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2021</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>9</VersionMinor>
|
||||
<VersionMinor>10</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
|
@ -127,4 +127,4 @@ When a release is created, if the PR ID number is linked inside the release desc
|
|||
- Issue message: 🎉This issue was addressed in #{pull request ID}, which has now been successfully released as {release name} {release version}.🎉"
|
||||
|
||||
## Admin Panel
|
||||
[Here](https://fabric-cp.azurewebsites.net/bot/)
|
||||
[Here](https://portal.fabricbot.ms/bot/?repo=microsoft/terminal)
|
||||
|
|
|
@ -9,7 +9,13 @@ git submodule update --init --recursive
|
|||
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory:
|
||||
|
||||
When using Visual Studio, be sure to set up the path for code formatting. This can be done in Visual Studio by going to Tools > Options > Text Editor > C++ > Formatting and checking "Use custom clang-format.exe file" and choosing the clang-format.exe in the repository at /dep/llvm/clang-format.exe by clicking "browse" right under the check box.
|
||||
When using Visual Studio, be sure to set up the path for code formatting. To download the required clang-format.exe file, follow one of the building instructions below and run:
|
||||
```powershell
|
||||
Import-Module .\tools\OpenConsole.psm1
|
||||
Set-MsBuildDevEnvironment
|
||||
Get-Format
|
||||
```
|
||||
After, go to Tools > Options > Text Editor > C++ > Formatting and checking "Use custom clang-format.exe file" in Visual Studio and choose the clang-format.exe in the repository at /packages/clang-format.win-x86.10.0.0/tools/clang-format.exe by clicking "browse" right under the check box.
|
||||
|
||||
### Building in PowerShell
|
||||
|
||||
|
|
|
@ -380,7 +380,7 @@ Here's the AppxManifest we're using:
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
</Dependencies>
|
||||
|
@ -517,7 +517,7 @@ This is because of a few key lines we already put in the appxmanifest:
|
|||
|
||||
```xml
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
</Dependencies>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$id": "https://github.com/microsoft/terminal/blob/main/doc/cascadia/profiles.schema.json",
|
||||
"$schema": "https://json-schema.org/draft/2019-09/schema#",
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema#",
|
||||
"title": "Microsoft's Windows Terminal Settings Profile Schema",
|
||||
"definitions": {
|
||||
"KeyChordSegment": {
|
||||
|
@ -37,7 +37,8 @@
|
|||
"type": "string",
|
||||
"enum": [
|
||||
"audible",
|
||||
"visual"
|
||||
"window",
|
||||
"taskbar"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -45,7 +46,8 @@
|
|||
"type": "string",
|
||||
"enum": [
|
||||
"audible",
|
||||
"visual",
|
||||
"taskbar",
|
||||
"window",
|
||||
"all",
|
||||
"none"
|
||||
]
|
||||
|
@ -188,6 +190,7 @@
|
|||
"duplicateTab",
|
||||
"find",
|
||||
"findMatch",
|
||||
"focusPane",
|
||||
"identifyWindow",
|
||||
"identifyWindows",
|
||||
"moveFocus",
|
||||
|
@ -798,6 +801,22 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"FocusPaneAction": {
|
||||
"description": "Arguments corresponding to a focusPane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "focusPane" },
|
||||
"id": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The ID of the pane to focus"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
|
@ -828,6 +847,7 @@
|
|||
{ "$ref": "#/definitions/PrevTabAction" },
|
||||
{ "$ref": "#/definitions/RenameTabAction" },
|
||||
{ "$ref": "#/definitions/RenameWindowAction" },
|
||||
{ "$ref": "#/definitions/FocusPaneAction" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
|
@ -900,6 +920,16 @@
|
|||
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied.",
|
||||
"$ref": "#/definitions/CopyFormat"
|
||||
},
|
||||
"trimBlockSelection": {
|
||||
"default": false,
|
||||
"description": "When set to true, trailing white-spaces will be removed from text in rectangular (block) selection while copied to your clipboard. When set to false, the white-spaces will be preserved.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.detectURLs": {
|
||||
"default": true,
|
||||
"description": "When set to true, URLs will be detected by the Terminal. This will cause URLs to underline on hover and be clickable by pressing Ctrl.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableAnimations": {
|
||||
"default": false,
|
||||
"description": "When set to `true`, visual animations will be disabled across the application.",
|
||||
|
@ -1166,7 +1196,7 @@
|
|||
},
|
||||
"bellStyle": {
|
||||
"default": "audible",
|
||||
"description": "Controls what happens when the application emits a BEL character. When set to \"all\", the Terminal will play a sound and flash the taskbar icon. An array of specific behaviors can also be used. Supported array values include `audible` and `visual`. When set to \"none\", nothing will happen.",
|
||||
"description": "Controls what happens when the application emits a BEL character. When set to \"all\", the Terminal will play a sound, flash the taskbar icon (if the terminal window is not in focus) and flash the window. An array of specific behaviors can also be used. Supported array values include `audible`, `window` and `taskbar`. When set to \"none\", nothing will happen.",
|
||||
"$ref": "#/definitions/BellStyle"
|
||||
},
|
||||
"closeOnExit": {
|
||||
|
@ -1347,6 +1377,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"]
|
||||
|
|
|
@ -9,7 +9,7 @@ issue id: #1043
|
|||
|
||||
## Abstract
|
||||
|
||||
This spec is for task #1043 “Be able to set an initial position for the terminal”. It goes over the details of a new feature that allows users to set the initial position and size of the terminal. Expected behavior and design of this feature is included. Besides, future possible follow-up works are also addressed.
|
||||
This spec is for task #1043 “Be able to set an initial position for the terminal”. It goes over the details of a new feature that allows users to set the initial position and size of the terminal. Expected behavior and design of this feature is included. Besides, future possible follow-up works are also addressed.
|
||||
|
||||
## Inspiration
|
||||
|
||||
|
@ -17,7 +17,7 @@ The idea is to allow users to set the initial position of the Terminal when they
|
|||
|
||||
## Solution Design
|
||||
|
||||
For now, the Terminal window is put on a default initial position. The program uses CW_USEDEFAULT in the screen coordinates for top-left corner. We have two different types of window – client window and non-client window. However, code path for window creation (WM_CREATE message is shared by the two types of windows) are almost the same for the two types of windows, except that there are some differences in calculation of the width and height of the window.
|
||||
For now, the Terminal window is put on a default initial position. The program uses CW_USEDEFAULT in the screen coordinates for top-left corner. We have two different types of window – client window and non-client window. However, code path for window creation (WM_CREATE message is shared by the two types of windows) are almost the same for the two types of windows, except that there are some differences in calculation of the width and height of the window.
|
||||
|
||||
Two new properties should be added in the json settings file:
|
||||
|
||||
|
@ -92,9 +92,9 @@ For now, this feature only allows the user to set initial position and choose wh
|
|||
|
||||
3. We may also consider more launch modes. Like full screen mode and minimized mode.
|
||||
|
||||
Github issue for future follow-ups: https://github.com/microsoft/terminal/issues/766
|
||||
GitHub issue for future follow-ups: https://github.com/microsoft/terminal/issues/766
|
||||
|
||||
## Resources
|
||||
|
||||
Github issue:
|
||||
GitHub issue:
|
||||
https://github.com/microsoft/terminal/issues/1043
|
||||
|
|
|
@ -123,4 +123,4 @@ This open issue tracks the phase features of Search: https://github.com/microsof
|
|||
|
||||
## Resources
|
||||
|
||||
Github Issue: https://github.com/microsoft/terminal/issues/605
|
||||
GitHub Issue: https://github.com/microsoft/terminal/issues/605
|
||||
|
|
736
doc/specs/#653 - Quake Mode/#653 - Quake Mode.md
Normal file
736
doc/specs/#653 - Quake Mode/#653 - Quake Mode.md
Normal file
|
@ -0,0 +1,736 @@
|
|||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2021-02-23
|
||||
last updated: 2021-05-13
|
||||
issue id: #653
|
||||
---
|
||||
|
||||
# Quake Mode
|
||||
|
||||
## Abstract
|
||||
|
||||
Many existing terminals support a feature whereby a user can press a keybinding
|
||||
anywhere in the OS, and summon their terminal application. Oftentimes the act of
|
||||
summoning this window is accompanied by a "dropdown" animation, where the window
|
||||
slides in to view from the top of the screen. This global summon action is often
|
||||
referred to as "quake mode", a reference to the video game Quake, who's console
|
||||
slid in from the top.
|
||||
|
||||
This spec addresses both of the following two issues:
|
||||
* "Quake Mode" ([#653])
|
||||
* "Minimize to tray" ([#5727])
|
||||
|
||||
Readers should make sure to have read the [Process Model 2.0 Spec], for
|
||||
background on Monarch and Peasant processes.
|
||||
|
||||
## Background
|
||||
|
||||
### Inspiration
|
||||
|
||||
For an example of the original Quake console in action, take a look at the
|
||||
following video (noisy video warning): [Quake 3 sample]. Additionally, plenty of
|
||||
existing terminal emulators support similar functionality:
|
||||
|
||||
* **Tilda** allows the user to specify different keys to summon the window on
|
||||
different monitors.
|
||||
* **Guake** alternatively allows the user to either summon the terminal window to
|
||||
a specific monitor, or whichever monitor the mouse is on. Guake only allows
|
||||
one single instance, so pressing the global hotkey always summons the same
|
||||
instance.
|
||||
|
||||
### User Stories
|
||||
|
||||
The original quake mode thread (#653) is absolutely _filled_ with variations on
|
||||
how users want to be able to summon their terminal windows. These include, but
|
||||
are not limited to:
|
||||
|
||||
* **Story A** Press a hotkey anywhere to activate the single Terminal window
|
||||
wherever it was
|
||||
* **Story B** Press a hotkey anywhere to activate the single Terminal window _on
|
||||
the current monitor_. If it wasn't previously on that monitor, move it there.
|
||||
* **Story C** When the Terminal is summoned using the hotkey, have it "slide in"
|
||||
from the top. Similarly, slide out on deactivate.
|
||||
* **Story D** <kbd>Ctrl+1</kbd> to activate the terminal on monitor 1,
|
||||
<kbd>Ctrl+2</kbd> to activate the terminal on monitor 2.
|
||||
* **Story E** Variable dropdown speed
|
||||
* **Story F** Minimize to tray, press a hotkey to activate the terminal window
|
||||
(#5727)
|
||||
* **Story G** Terminal doesn't appear in alt+tab view, press a hotkey to
|
||||
activate the single terminal window / the nearest terminal window (I'm not
|
||||
sure this is distinct from the above)
|
||||
|
||||
## Solution Design
|
||||
|
||||
To implement this feature, we'll add the following settings:
|
||||
* a new action, named `globalSummon`.
|
||||
* a new global, app setting named `minimizeToTray`
|
||||
* a new global, app setting named `alwaysShowTrayIcon`
|
||||
* a new action, named `quakeMode`, and a specially named `_quake` window.
|
||||
|
||||
### `globalSummon` Action
|
||||
|
||||
The `globalSummon` action will be a keybinding the user can use to summon a
|
||||
Terminal window from anywhere in the OS. Various arguments to the action will
|
||||
specify which window is summoned, to where, and how the window should behave on
|
||||
summon.
|
||||
|
||||
From a technical perspective, the action will work by using the
|
||||
[`RegisterHotKey`] function. This API allows us to bind a particular hotkey with
|
||||
the OS. Whenever that hotkey is pressed, our window message loop will receive a
|
||||
`WM_HOTKEY`. We'll use the payload of that window message to lookup the action
|
||||
arguments for that hotkey. Then we'll use those arguments to control which
|
||||
window is invoked, where, and how the window behaves.
|
||||
|
||||
Since `RegisterHotKey` can only be used to register a hotkey _once_ with the OS,
|
||||
we'll need to make sure it's only ever set up by the Monarch process. We know
|
||||
that there will only ever be one Monarch for the Terminal at a time, so it's the
|
||||
perfect process to have the responsibility of managing the global hotkey.
|
||||
|
||||
The Monarch will be responsible for calling `RegisterHotKey`, and processing the
|
||||
`WM_HOTKEY` messages. It will then dispatch method calls to the appropriate
|
||||
window to summon it. When a Monarch dies and a new process becomes the Monarch,
|
||||
the new Monarch will re-register for the hotkeys.
|
||||
|
||||
#### Where in the settings?
|
||||
|
||||
Since users may want to bind multiple keys to summon different windows, we'll
|
||||
need to allow the user to specify multiple keybindings simultaneously, each with
|
||||
their own set of args.
|
||||
|
||||
We stick all the `globalSummon`s in the actions array, like they're any other
|
||||
keybinding.
|
||||
|
||||
However, these are not keys that are handled by the TerminalApp layer itself.
|
||||
These are keys that need to be registered with the OS. So while they will be in
|
||||
the normal `KeyMap`, they will need to be retrieved from that object and
|
||||
manually passed to the window layer.
|
||||
|
||||
> A previous iteration of this spec considered placing the `globalSummon`
|
||||
> actions in their own top-level array of the settings file, separate from the
|
||||
> keybindings. This is no longer being considered, because it would not work for
|
||||
> the case where the user has something like:
|
||||
> ```json
|
||||
> { "keys": "ctrl+c", "command": { "action": "globalSummon", "monitor": 1 } },
|
||||
> { "keys": "ctrl+v", "command": { "action": "copy" } },
|
||||
> ```
|
||||
|
||||
#### Which window, and where?
|
||||
|
||||
When looking at the list of requested scenarios, there are lots of different
|
||||
ways people would like to use the global summon action. Some want the most
|
||||
recent window activated, always. Others want to have one window _per monitor_.
|
||||
Some would like to move the window to where the user is currently interacting
|
||||
with the PC, and others want to activate the window where it already exists.
|
||||
Trying to properly express all these possible configurations is complex. The
|
||||
settings should be unambiguous as to what will happen when you press the
|
||||
keybinding.
|
||||
|
||||
I believe that in order to accurately support all the variations that people
|
||||
might want, we'll need two properties in the `globalSummon` action. These
|
||||
properties will specify _which_ window we're summoning, and _where_ to summon
|
||||
the window. To try and satisfy all these scenarios, I'm proposing the following
|
||||
two arguments to the `globalSummon` action:
|
||||
|
||||
```json
|
||||
"monitor": "any"|"toCurrent"|"toMouse"|"onCurrent"|int,
|
||||
"desktop": "any"|"toCurrent"|"onCurrent"
|
||||
```
|
||||
|
||||
The way these settings can be combined is in a table below. As an overview:
|
||||
|
||||
* `monitor`: This controls the monitor that the window will be summoned from/to
|
||||
- `"any"`: Summon the MRU window, regardless of which monitor it's currently
|
||||
on.
|
||||
- `"toCurrent"`/omitted: (_default_): Summon the MRU window **TO** the monitor
|
||||
with the current **foreground** window.
|
||||
- `"toMouse"`: Summon the MRU window **TO** the monitor where the **mouse**
|
||||
cursor is.
|
||||
- `"onCurrent"`: Summon the MRU window **ALREADY ON** the current monitor.
|
||||
- `int`: Summon the MRU window for the given monitor (as identified by the
|
||||
"Identify" displays feature in the OS settings)
|
||||
|
||||
* `desktop`: This controls how the terminal should interact with virtual desktops.
|
||||
- `"any"`: Leave the window on whatever desktop it's already on - we'll switch
|
||||
to that desktop as we activate the window.
|
||||
- > NOTE: A previous version of this spec had this enum value as `null`.
|
||||
This was changed to `"any"` for parity with the `monitor` property.
|
||||
- `"toCurrent"`/omitted: (_default_): Move the window **to** the current
|
||||
virtual desktop
|
||||
- `"onCurrent"`: Only summon the window if it's **already on** the current
|
||||
virtual desktop
|
||||
|
||||
Neither `desktop` nor `monitor` is a required parameter - if either is omitted,
|
||||
the omitted property will default to `toCurrent`.
|
||||
|
||||
Together, these settings interact in the following ways:
|
||||
|
||||
<!-- This table is formatted for viewing as rendered HTML. It's too complicated
|
||||
for pure markdown, sorry. -->
|
||||
<table>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th colspan=3><code>"desktop"</code></th>
|
||||
</tr>
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<tr>
|
||||
<th><code>"monitor"</code></th>
|
||||
<td><code>any</code><br><strong>Leave where it is</strong></td>
|
||||
<td><code>"toCurrent"</code><br><strong>Move to current desktop</strong></td>
|
||||
<td><code>"onCurrent"</code><br><strong>On current desktop only</strong></td>
|
||||
</tr>
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<tr>
|
||||
<td><code>"any"</code><br> Summon the MRU window</td>
|
||||
|
||||
<td>Go to the desktop the window is on (leave position alone)</td>
|
||||
<td>Move the window to this desktop (leave position alone)</td>
|
||||
<td>
|
||||
|
||||
If there isn't one on this desktop:
|
||||
* create a new one (default position)
|
||||
|
||||
Else:
|
||||
* activate the one on this desktop (don't move it)
|
||||
</td>
|
||||
</tr>
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<tr>
|
||||
<td><code>"toCurrent"</code><br> Summon the MRU window TO the monitor with the foreground window</td>
|
||||
<td>Go to the desktop the window is on, move to the monitor with the foreground window</td>
|
||||
<td>Move the window to this desktop, move to the monitor with the foreground window</td>
|
||||
<td>
|
||||
|
||||
If there isn't one on this desktop:
|
||||
* create a new one (on the monitor with the foreground window)
|
||||
|
||||
Else:
|
||||
* activate the one on this desktop, move to the monitor with the foreground window
|
||||
</td>
|
||||
</tr>
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<tr>
|
||||
<td>
|
||||
<code>"toMouse"</code>
|
||||
<sup><a href="#footnote-2">[2]</a></sup> <br>
|
||||
Summon the MRU window TO the monitor with the mouse</td>
|
||||
<td>Go to the desktop the window is on, move to the monitor with the mouse</td>
|
||||
<td>Move the window to this desktop, move to the monitor with the mouse</td>
|
||||
<td>
|
||||
|
||||
If there isn't one on this desktop:
|
||||
* create a new one (on the monitor with the mouse)
|
||||
|
||||
Else:
|
||||
* activate the one on this desktop, move to the monitor with the mouse
|
||||
</td>
|
||||
</tr>
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<tr>
|
||||
<td><code>"onCurrent"</code><br> Summon the MRU window for the current monitor</td>
|
||||
<td>
|
||||
|
||||
If there is a window on this monitor on any desktop,
|
||||
* Go to the desktop the window is on (leave position alone)
|
||||
|
||||
else
|
||||
* Create a new window on this monitor & desktop
|
||||
</td>
|
||||
<td>
|
||||
|
||||
If there is a window on this monitor on any desktop,
|
||||
* Move the window to this desktop (leave position alone)
|
||||
|
||||
else
|
||||
* Create a new window on this monitor & desktop
|
||||
</td>
|
||||
<td>
|
||||
|
||||
If there isn't one on this desktop, (even if there is one on this monitor on
|
||||
another desktop),
|
||||
* create a new one on this monitor
|
||||
|
||||
Else if ( there is one on this desktop, not this monitor)
|
||||
* create a new one on this monitor
|
||||
|
||||
Else (one on this desktop & monitor)
|
||||
* Activate the one on this desktop (don't move)
|
||||
</td>
|
||||
</tr>
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<tr>
|
||||
<td><code>int</code><br> Summon the MRU window for monitor N</td>
|
||||
<td>
|
||||
|
||||
If there is a window on monitor N on any desktop,
|
||||
* Go to the desktop the window is on (leave position alone)
|
||||
|
||||
else
|
||||
* Create a new window on this monitor & desktop
|
||||
</td>
|
||||
<td>
|
||||
|
||||
If there is a window on monitor N on any desktop,
|
||||
* Move the window to this desktop (leave position alone)
|
||||
|
||||
else
|
||||
* Create a new window on this monitor & desktop
|
||||
</td>
|
||||
<td>
|
||||
|
||||
If there isn't one on this desktop, (even if there is one on monitor N on
|
||||
another desktop),
|
||||
* create a new one on monitor N
|
||||
|
||||
Else if ( there is one on this desktop, not monitor N)
|
||||
* create a new one on monitor N
|
||||
|
||||
Else (one on this desktop & monitor N)
|
||||
* Activate the one on this desktop (don't move)
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
##### Stories, revisited
|
||||
|
||||
With the above settings, let's re-examine the original user stories, and see how
|
||||
they fit into the above settings. (_Stories that are omitted aren't relevant to
|
||||
the discussion of these settings_)
|
||||
|
||||
> When the `desktop` param is omitted below, that can be interpreted as "any
|
||||
> `desktop` value will make sense here"
|
||||
|
||||
* **Story A** Press a hotkey anywhere to activate the single Terminal window
|
||||
wherever it was
|
||||
- This is `{ "monitor": "any", "desktop": "any" }`
|
||||
* **Story B** Press a hotkey anywhere to activate the single Terminal window _on
|
||||
the current monitor_. If it wasn't previously on that monitor, move it there.
|
||||
- This is `{ "monitor": "toCurrent" }`
|
||||
* **Story D** <kbd>Ctrl+1</kbd> to activate the terminal on monitor 1,
|
||||
<kbd>Ctrl+2</kbd> to activate the terminal on monitor 2.
|
||||
- This is `[ { "keys": "ctrl+1", monitor": 1 }, { "keys": "ctrl+2", monitor": 2 } ]`
|
||||
|
||||
As some additional examples:
|
||||
|
||||
```json
|
||||
// Go to the MRU window, wherever it is
|
||||
{ "keys": "win+1", "command":{ "action":"globalSummon", "monitor":"any", "desktop": "any" } },
|
||||
|
||||
// activate the MRU window, and move it to this desktop & this monitor
|
||||
{ "keys": "win+2", "command":{ "action":"globalSummon", "monitor":"toCurrent", "desktop": "toCurrent" } },
|
||||
// Since "toCurrent" & "toCurrent" are the default values, just placing a single
|
||||
// entry here will bind the same behavior:
|
||||
{ "keys": "win+2", "command": "globalSummon" },
|
||||
|
||||
// activate the MRU window on this desktop
|
||||
{ "keys": "win+3", "command":{ "action":"globalSummon", "monitor":"any", "desktop": "onCurrent" } },
|
||||
|
||||
// Activate the MRU window on monitor 2 (from any desktop), and place it on the
|
||||
// current desktop. If there isn't one on monitor 2, make a new one.
|
||||
{ "keys": "win+4", "command":{ "action":"globalSummon", "monitor": 2, "desktop": "toCurrent" } },
|
||||
|
||||
// Activate the MRU window on monitor 3 (ONLY THIS desktop), or make a new one.
|
||||
{ "keys": "win+5", "command":{ "action":"globalSummon", "monitor": 3, "desktop": "onCurrent" } },
|
||||
|
||||
// Activate the MRU window on this monitor (from any desktop), and place it on
|
||||
// the current desktop. If there isn't one on this monitor, make a new one.
|
||||
{ "keys": "win+6", "command":{ "action":"globalSummon", "monitor": "onCurrent", "desktop": "toCurrent" } },
|
||||
```
|
||||
|
||||
#### Summoning a specific window
|
||||
|
||||
What if you want to press a keybinding to always summon a specific, named
|
||||
window? This window might not be the most recent terminal window, nor one that
|
||||
would be selected by the `monitor` and `desktop` selectors. You could name a
|
||||
window "Epona", and press `win+e` to always summon the "Epona" window.
|
||||
|
||||
We'll add the following property to address this scenario
|
||||
|
||||
* `"window": string|int`
|
||||
- When omitted (_default_): Use monitor and desktop to find the appropriate
|
||||
MRU window to summon.
|
||||
- When provided: Always summon the window who's name or ID matches the given
|
||||
`window` value. If no such window exists, then create a new window with that
|
||||
name/id.
|
||||
|
||||
When provided _with_ `monitor` and `desktop`, `window` behaves in the following
|
||||
ways:
|
||||
* `desktop`
|
||||
- `"any"`: Go to the desktop the given window is already on.
|
||||
- `"toCurrent"`: If the window is on another virtual desktop, then move it to
|
||||
the currently active one.
|
||||
- `"onCurrent"`: If the window is on another virtual desktop, then move it to
|
||||
the currently active one.
|
||||
* `monitor`
|
||||
- `"any"`: Leave the window on the monitor it is already on.
|
||||
- `"toCurrent"`: If the window is on another monitor, then move it to the
|
||||
currently active one.
|
||||
- `"onCurrent"`: If the window is on another monitor, then move it to the
|
||||
currently active one.
|
||||
- `<int>`: If the window is on another monitor, then move it to the specified
|
||||
monitor.
|
||||
|
||||
> NOTE: You read that right, `onCurrent` and `toCurrent` both do the same thing
|
||||
> when `window` is provided. They both already know which window to select, the
|
||||
> context of moving to the "current" monitor is all that those parameters add.
|
||||
|
||||
#### Other properties
|
||||
|
||||
Some users would like the terminal to just appear when the global hotkey is
|
||||
pressed. Others would like the true quake-like experience, where the terminal
|
||||
window "slides-in" from the top of the monitor. Furthermore, some users would
|
||||
like to configure the speed at which that dropdown happens. To support this
|
||||
functionality, the `globalSummon` action will support the following property:
|
||||
|
||||
* `"dropdownDuration": float`
|
||||
- When omitted, `0`, or a negative number: No animation is used
|
||||
when summoning the window. The summoned window is focused immediately where
|
||||
it is.
|
||||
- When a positive number is provided, the terminal will use that value as a
|
||||
duration (in seconds) to slide the terminal into position when activated.
|
||||
- The default would be some sensible value. The pane animation is .2s, so
|
||||
`0.2` might be a reasonable default here.
|
||||
|
||||
We could have alternatively provided a `"dropdownSpeed"` setting, that provided
|
||||
a number of pixels per second. In my opinion, that would be harder for users to
|
||||
use correctly. I believe that it's easier for users to mentally picture "I'd
|
||||
like the dropdown to last 100ms" vs "My monitor is 1504px tall, so I need to set
|
||||
this to 15040 to make the window traverse the entire display in .1s"
|
||||
|
||||
> NOTE: `dropdownDuration` will be ignored when the user has animations disabled
|
||||
> in the OS. In that case, the terminal will just appear, as if it was set to 0.
|
||||
|
||||
Some users might want to be able to use the global hotkey to hide the window
|
||||
when the window is already visible. This would let the hotkey act as a sort of
|
||||
global toggle for the Terminal window. Others might not like that behavior, and
|
||||
just want the action to always bring the Terminal into focus, and do nothing if
|
||||
the terminal is already focused. To facilitate both these use cases, we'll add
|
||||
the following property:
|
||||
|
||||
* `"toggleVisibility": bool`
|
||||
- When `true`: (_default_) When this hotkey is pressed, and the terminal
|
||||
window is currently active, minimize the window.
|
||||
- When `dropdownDuration` is not `0`, then the window will slide back off
|
||||
the top at the same speed as it would come down.
|
||||
- When `false`: When this hotkey is pressed, and the terminal window is
|
||||
currently active, do nothing.
|
||||
|
||||
### Quake Mode
|
||||
|
||||
In addition to just summoning the window from anywhere, some terminals also
|
||||
support a special "quake mode" buffer or window. This window is one that closely
|
||||
emulates the console from quake:
|
||||
* It's docked to the top of the screen
|
||||
* It takes the full width of the monitor, and only the bottom can be resized
|
||||
* It often doesn't have any other UI elements, like tabs
|
||||
|
||||
For fun, we'll also be adding a special `"_quake"` window with the same
|
||||
behavior. If the user names a window `_quake`, then it will behave in the
|
||||
following special ways:
|
||||
|
||||
* On launch, it will ignore the `initialPosition` and
|
||||
`initialRows`/`initialCols` setting, and instead resize to the top half of the
|
||||
monitor.
|
||||
* On launch, it will ignore the `launchMode` setting, and always launch in focus
|
||||
mode.
|
||||
- Users can disable focus mode on the `_quake` window if they do want tabs.
|
||||
* It will not be resizable from any side except the bottom of the window, nor
|
||||
will it be drag-able.
|
||||
* It will not be a valid target for the "most recent window" for window
|
||||
glomming. If it's the only open window, with `"windowingBehavior":
|
||||
"useExisting*"`, then a new window will be created instead.
|
||||
- It _is_ still a valid target for something like `wt -w _quake new-tab`
|
||||
|
||||
A window at runtime can be renamed to become the `_quake` window (if no other
|
||||
`_quake` window exists). When it does, it will resize to the position of the
|
||||
quake window, and enter focus mode.
|
||||
|
||||
We'll also be adding a special action `quakeMode`. This action is a special case
|
||||
of the `globalSummon` action, to specifically invoke the quake window in the
|
||||
current place. It is basically the same thing as the more elaborate:
|
||||
|
||||
```json
|
||||
{
|
||||
"monitor": "toCurrent",
|
||||
"desktop": "toCurrent",
|
||||
"window": "_quake",
|
||||
"toggleVisibility": true,
|
||||
"dropdownDuration": 0.5
|
||||
},
|
||||
```
|
||||
|
||||
### Minimize to Tray
|
||||
|
||||
Many users have requested that the terminal additionally supports minimizing the
|
||||
window "to the tray icon". This is a bit like when you close the Teams window,
|
||||
but Teams is actually still running in the system tray, or the "notification
|
||||
area".
|
||||
|
||||
![The Teams tray icon](tray-icon-000.png)
|
||||
|
||||
_fig 1: an example of the Teams tray icon in the notification area_.
|
||||
|
||||
When users want to be able to "minimize to the tray", they want:
|
||||
* The window to no longer appear on the taskbar
|
||||
* The window to no longer appear in the alt-tab order
|
||||
|
||||
When minimized to the tray, it's almost as if there's no window for the Terminal
|
||||
at all. This can be combined with the global hotkey (or the tray icon's context
|
||||
menu) to quickly restore the window.
|
||||
|
||||
The tray icon could be used for a variety of purposes. As a simple start, we
|
||||
could include the following three options:
|
||||
|
||||
```
|
||||
Focus Terminal
|
||||
---
|
||||
Windows > Window 1 - <un-named window>
|
||||
Window 2 - "This-window-does-have-a-name"
|
||||
---
|
||||
Quit
|
||||
```
|
||||
|
||||
Just clicking on the icon would summon the recent terminal window. Right
|
||||
clicking would show the menu with "Focus Terminal", "Windows" and "Quit" in it, and
|
||||
"Windows" would have nested entries for each Terminal window.
|
||||
|
||||
* "Focus Terminal" would do just that - summon the most recent terminal window,
|
||||
wherever it is.
|
||||
* "Windows" would have nested popups for each open Terminal window. Each of
|
||||
these nested entries would display the name and ID of the window. Clicking
|
||||
them would summon that window (wherever it may be)
|
||||
* "Quit" would be akin to quit in browsers - close all open windows
|
||||
<sup>[[1]](#footnote-1)</sup>.
|
||||
|
||||
The tray notification would be visible always when the user has
|
||||
`"minimizeToTray": true` set in their settings. If the user has that set to
|
||||
false, but would still like the tray, they can specify `"alwaysShowTrayIcon":
|
||||
true`. That will cause the tray icon to always be added to the system tray.
|
||||
|
||||
There's not a combination of settings where the Terminal is "minimized to the
|
||||
tray", and there's _no tray icon visible_. We don't want to let users get into a
|
||||
state where the Terminal is running, but is totally hidden from their control.
|
||||
|
||||
From a technical standpoint, the tray icon is managed similar to the global
|
||||
hotkey. The Monarch process is responsible for setting it up, and processing the
|
||||
messages. When a Monarch dies and a new process becomes the Monarch, then it
|
||||
will re-create the tray icon.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
To summarize, we're proposing the following set of settings:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"minimizeToTray": bool,
|
||||
"alwaysShowTrayIcon": bool,
|
||||
"actions": [
|
||||
{
|
||||
"keys": KeyChord,
|
||||
"command": {
|
||||
"action": "globalSummon",
|
||||
"dropdownDuration": float,
|
||||
"toggleVisibility": bool,
|
||||
"monitor": "any"|"toCurrent"|"onCurrent"|int,
|
||||
"desktop": "any"|"toCurrent"|"onCurrent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"keys": KeyChord,
|
||||
"command": {
|
||||
"action": "quakeMode"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Potential Issues
|
||||
|
||||
<table>
|
||||
|
||||
<tr>
|
||||
<td><strong>Compatibility</strong></td>
|
||||
<td>
|
||||
|
||||
As part of this set of changes, we'll also be allowing the <kbd>Win</kbd> key in
|
||||
keybindings. Generally, the OS reserves the Windows key for its own shortcuts.
|
||||
For example, <kbd>Win+R</kbd> for the run dialog, <kbd>Win+A</kbd> for the
|
||||
Action Center, <kbd>Win+V</kbd> for the cloud clipboard, etc. Users will now be
|
||||
able to use the win key themselves, but they should be aware that the OS has
|
||||
"first dibs" on any hotkeys involving the Windows key.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Mixed elevation</strong></td>
|
||||
<td>
|
||||
|
||||
Only one app at a time gets to register for global hotkeys. However, from the
|
||||
Terminal's perspective, unelevated and elevated windows will act like different
|
||||
apps. Each privilege level has its own Monarch. The two are unable to
|
||||
communicate across the elevation boundary.
|
||||
|
||||
This means that if the user often runs terminals in both contexts, then only one
|
||||
will have the global hotkeys bound. The naive implementation would have the
|
||||
first elevation level "win" the keybindings.
|
||||
|
||||
A different option would be to have elevated windows not register global hotkeys
|
||||
_at all_. I don't believe that there's any sort of security implication for
|
||||
having a global hotkey for an elevated window.
|
||||
|
||||
A third option would be to have some sort of `"whenElevated": bool?` property
|
||||
for global hotkeys. This would explicitly enable a given hotkey for unelevated
|
||||
vs elevated windows.
|
||||
* `"whenElevated": null`: behave as normal - the first context level to run wins
|
||||
* `"whenElevated": true`: only register the hotkey when running elevated
|
||||
* `"whenElevated": false`: only register the hotkey when running unelevated
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>OneCore / Windows 10X</strong></td>
|
||||
<td>
|
||||
|
||||
I'm fairly certain that none of these APIs would work on Windows 10X at all.
|
||||
These features would have to initially be disabled in a pure UWP version of the
|
||||
Terminal, until we could find workarounds. Since the window layer is the one
|
||||
responsible for the management of the hotkeys and the tray icon, we're not too
|
||||
worried about this.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
* If there are any other applications running that have already registered
|
||||
hotkeys with `RegisterHotKey`, then it's possible that the Terminal's attempt
|
||||
to register that hotkey will fail. If that should happen, then we should
|
||||
display a warning dialog to the user indicating which hotkey will not work
|
||||
(because it's already used for something else).
|
||||
|
||||
* Which is the "current" monitor? The one with the mouse or the one with the
|
||||
active window? This isn't something that has an obvious answer. Guake
|
||||
implements this feature where the "current monitor" is the one with the mouse
|
||||
on it. At least for the first iterations of this action, that's what we'll
|
||||
use.
|
||||
`monitor: onCurrent|onCurrentWindow|toCurrent|<int>`
|
||||
|
||||
* Currently, running both the Release and Preview versions of the Terminal at
|
||||
the same time side-by-side is not generally supported. (For example, `wt.exe`
|
||||
can only ever point at one of two.) If a user binds the same key to a
|
||||
`globalSummon` or `quakeMode` action, then only one of the apps will actually
|
||||
be able to successfully claim the global hotkey.
|
||||
|
||||
## Implementation plan
|
||||
|
||||
Currently, in [`dev/migrie/f/653-QUAKE-MODE`], I have some sample rudimentary
|
||||
code to implement quake mode support. It allows for only a single global hotkey
|
||||
that summons the MRU window, without dropdown. That would be a good place for
|
||||
anyone starting to work on this feature. From there, I imagine the following
|
||||
work would be needed:
|
||||
|
||||
* [ ] Add a `globalSummon` action. `AppHost` would need to be able to get _all_
|
||||
of these actions, and register all of them. Each one would need to be assigned
|
||||
a unique ID, so `WM_HOTKEY` can identify which hotkey was pressed.
|
||||
- This could be committed without any other args to the `globalHotkeys`. In
|
||||
this initial version, the behavior would be summoning the MRU window,
|
||||
where it is, no dropdown, to start with. From there, we'd add the
|
||||
remaining properties:
|
||||
* [ ] Add support for the `toggleVisibility` property
|
||||
* [ ] Add support for the `desktop` property to control how window summoning
|
||||
interacts with virtual desktops
|
||||
* [ ] Add support for the `monitor` which monitor the window appears on.
|
||||
* [ ] Add support for the `dropdownDuration` property
|
||||
* [ ] Add the `minimizeToTray` setting, and implement it without any sort of flyout
|
||||
* [ ] Add a list of windows to the right-click flyout on the tray icon
|
||||
* [ ] Add support for the `alwaysShowTrayIcon` setting
|
||||
* [ ] When the user creates a window named `_quake`, ignore the initial size,
|
||||
position, and launch mode, and create the window in quake mode instead.
|
||||
* [ ] Exempt the `_quake` window from window glomming
|
||||
* [ ] Add the `quakeMode` action, which `globalSummon`'s the `_quake` window.
|
||||
* [ ] Prevent the `_quake` window from being dragged or resized on the
|
||||
top/left/right.
|
||||
|
||||
|
||||
### Future Considerations
|
||||
|
||||
I don't believe there are any other tracked requests that are planned that
|
||||
aren't already included in this spec.
|
||||
|
||||
* Should the tray icon's list of windows include window titles? Both the name
|
||||
and title? Maybe something like `({name}|{id}): {title}`? I'd bet that most
|
||||
people don't end up naming their windows.
|
||||
* Dropdown duration could be a `float|bool`, with `true`->(whatever the default
|
||||
is), `false`->0.
|
||||
- We could have the setting appear as a pair of radio buttons, with the first
|
||||
disabling dropdown, and the second enabling a text box for inputting an
|
||||
animation duration.
|
||||
* It might be an interesting idea to have the ability to dock the quake window
|
||||
to a specific side of the monitor, not just the top. We could probably do that
|
||||
with a global setting `"quakeModeDockSide": "top"|"left"|"bottom"|"right"` or
|
||||
something like that.
|
||||
* We might want to pre-load the quake window into the tray icon as an entry for
|
||||
"Quake Mode", and otherwise exclude it from the list of windows in that menu.
|
||||
* We might think of other things for the Quake Mode window in the future - this
|
||||
spec is by no means comprehensive. For example, it might make sense for the
|
||||
quake mode window to automatically open in "always on top" mode.
|
||||
* It was suggested that the quake mode window could auto-hide when it loses
|
||||
focus. That's a really neat idea, but we're a little worried about the
|
||||
implementation. What happens when the IME window gets focus? Or the Start
|
||||
Menu? Would those end up causing the quake window to prematurely minimize
|
||||
itself? For that reason, we're leaving this as a future consideration.
|
||||
* Perhaps there could be a top-level object in the settings like
|
||||
```json
|
||||
{
|
||||
"quakeMode": {
|
||||
"hideOnFocusLost": true,
|
||||
"useFocusMode": false,
|
||||
"profile": "my quake mode profile" ,
|
||||
"quakeModeDockSide": "bottom"
|
||||
}
|
||||
}
|
||||
```
|
||||
That would allow the user some further customizations on the quake mode
|
||||
behaviors.
|
||||
- This was later converted to the idea in [#9992] - Add per-window-name global
|
||||
settings
|
||||
* Another proposed idea was a simplification of some of the summoning modes. `{
|
||||
"monitor": "any", "desktop": "any" }` is a little long, and maybe not the most
|
||||
apparent naming. Perhaps we could add another property like `summonMode` that
|
||||
would act like an alias for a `monitor`, `desktop` combo.
|
||||
- `"summonMode": "activateInMyFace"`: `{ "monitor": "toCurrent", "desktop": "toCurrent" }`
|
||||
- `"summonMode": "activateWherever"`: `{ "monitor": "any", "desktop": "any" }`
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
Docs on adding a system tray item:
|
||||
* https://docs.microsoft.com/en-us/windows/win32/shell/notification-area
|
||||
* https://www.codeproject.com/Articles/18783/Example-of-a-SysTray-App-in-Win32
|
||||
|
||||
Docs regarding hiding a window from the taskbar:
|
||||
* https://docs.microsoft.com/en-us/previous-versions//bb776822(v=vs.85)#managing-taskbar-buttons
|
||||
|
||||
### Footnotes
|
||||
|
||||
<a name="footnote-1"><a>[1]: Quitting the terminal is different than closing the
|
||||
windows one-by-one. Quiting implies an atomic action, for closing all the
|
||||
windows. Once [#766] lands, this will give us a chance to persist the state of
|
||||
_all_ open windows. This will allow us to re-open with all the user's windows,
|
||||
not just the one that happened to be closed last.
|
||||
|
||||
<a name="footnote-2"><a>[2]: **Addenda, May 2021**: In the course of
|
||||
implementation, it became apparent that there's an important UX difference
|
||||
between summoning _to the monitor with the cursor_ vs _to the monitor with the
|
||||
foreground window_. `"monitor": "toMouse"` was added as an option, to allow the
|
||||
user to differentiate between the two behaviors.
|
||||
|
||||
[#653]: https://github.com/microsoft/terminal/issues/653
|
||||
[#766]: https://github.com/microsoft/terminal/issues/766
|
||||
[#5727]: https://github.com/microsoft/terminal/issues/5727
|
||||
[#9992]: https://github.com/microsoft/terminal/issues/9992
|
||||
|
||||
[Process Model 2.0 Spec]: https://github.com/microsoft/terminal/blob/main/doc/specs/%235000%20-%20Process%20Model%202.0/%235000%20-%20Process%20Model%202.0.md
|
||||
[Quake 3 sample]: https://youtu.be/ZmR6HQbuHPA?t=27
|
||||
[`RegisterHotKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey
|
||||
[`dev/migrie/f/653-QUAKE-MODE`]: https://github.com/microsoft/terminal/tree/dev/migrie/f/653-QUAKE-MODE
|
BIN
doc/specs/#653 - Quake Mode/tray-icon-000.png
Normal file
BIN
doc/specs/#653 - Quake Mode/tray-icon-000.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
344
doc/specs/#885 - Terminal Settings Model/Actions Addendum.md
Normal file
344
doc/specs/#885 - Terminal Settings Model/Actions Addendum.md
Normal file
|
@ -0,0 +1,344 @@
|
|||
---
|
||||
author: Carlos Zamora @carlos-zamora
|
||||
created on: 2021-03-12
|
||||
last updated: 2021-03-17
|
||||
issue id: [#885]
|
||||
---
|
||||
|
||||
# Actions in the Settings Model
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec proposes a refactor of how Windows Terminal actions are stored in the settings model.
|
||||
The new representation would mainly allow the serialization and deserialization of commands and keybindings.
|
||||
|
||||
## Inspiration
|
||||
|
||||
A major component that is missing from the Settings UI is the representation of keybindings and commands.
|
||||
The JSON represents both of these as a combined entry as follows:
|
||||
```js
|
||||
{ "icon": "path/to/icon.png", "name": "Copy the selected text", "command": "copy", "keys": "ctrl+c" },
|
||||
```
|
||||
In the example above, the copy action is...
|
||||
- bound to <kbd>ctrl+c</kbd>
|
||||
- presented as "Copy the selected text" with the "path/to/icon.png" icon
|
||||
|
||||
However, at the time of writing this spec, the settings model represents it as...
|
||||
- (key binding) a `KeyChord` to `ActionAndArgs` entry in a `KeyMapping`
|
||||
- (command) a `Command` with an associated icon, name, and action (`ActionAndArgs`)
|
||||
|
||||
This introduces the following issues:
|
||||
1. Serialization
|
||||
- We have no way of knowing when a command and a key binding point to the same action. Thus, we don't
|
||||
know when to write a "name" to the json.
|
||||
- We also don't know if the name was auto-generated or set by the user. This can make the JSON much more bloated by
|
||||
actions with names that would normally be autogenerated.
|
||||
2. Handling Duplicates
|
||||
- The same action can be bound to multiple key chords. The command palette combines all of these actions into one entry
|
||||
because they have the same name. In reality, this same action is just being referenced in different ways.
|
||||
|
||||
## Solution Design
|
||||
|
||||
I propose that the issues stated above be handled via the following approach.
|
||||
|
||||
### Step 1: Consolidating actions
|
||||
|
||||
`Command` will be updated to look like the following:
|
||||
|
||||
```c++
|
||||
runtimeclass Command
|
||||
{
|
||||
// The path to the icon (or icon itself, if it's an emoji)
|
||||
String IconPath;
|
||||
|
||||
// The associated name. If none is defined, one is auto-generated.
|
||||
String Name;
|
||||
|
||||
// The key binding that can be used to invoke this action.
|
||||
// NOTE: We're actually holding the KeyChord instead of just the text.
|
||||
// KeyChordText just serializes the relevant keychord
|
||||
Microsoft.Terminal.Control.KeyChord Keys;
|
||||
String KeyChordText;
|
||||
|
||||
// The action itself.
|
||||
ActionAndArgs ActionAndArgs;
|
||||
|
||||
// NOTE: nested and iterable command logic will still be here,
|
||||
// But they are omitted to make this section seem cleaner.
|
||||
|
||||
// Future Considerations:
|
||||
// - [#6899]: Action IDs --> add an identifier here
|
||||
}
|
||||
```
|
||||
|
||||
The goal here is to consolidate key binding actions and command palette actions into a single class.
|
||||
This will also require the following supplemental changes:
|
||||
- `Command::LayerJson`
|
||||
- This must combine the logic of `KeyMapping::LayerJson` and `Command::LayerJson`.
|
||||
- Key Chord data
|
||||
- Internally, store a `vector<KeyChord> _keyMappings` to keep track of all the key chords associated with this action.
|
||||
- `RegisterKey` and `EraseKey` update `_keyMappings`, and ensure that the latest key registered is at the end of the list.
|
||||
- `Keys()` simply returns the last entry of `_keyMappings`, which is the latest key chord this action is bound to.
|
||||
- `KeyChordText())` is exposed to pass the text directly to the command palette.
|
||||
This depends on `Keys` and, thus, propagate changes automatically.
|
||||
- Observable properties
|
||||
- `Command` has observable properties today, but does not need them because `Command` will never change while the app is running.
|
||||
- Nested and iterable commands
|
||||
- `HasNestedCommands`, `NestedCommands{ get; }`, `IterateOn` will continue to be exposed.
|
||||
- A setter for these customizations will not be exposed until we find it necessary (i.e. adding support for customizing it in the Settings UI)
|
||||
- Command expansion can continue to be exposed here to reduce implementation cost.
|
||||
- An additional `IsNestedCommand` is necessary to record a case where a nested command is being unbound `{ "commands": null, "name": "foo" }`.
|
||||
|
||||
Overall, the `Command` class is simply being promoted to include the `KeyChord` it has.
|
||||
This allows the implementation cost of this step to be relatively small.
|
||||
|
||||
Completion of this step should only cause relatively minor changes to anything that depends on `Command`, because
|
||||
it is largely the same class. However, key bindings will largely be impacted because we represent key bindings as
|
||||
a map of `KeyChord`s to `ActionAndArgs`. This leads us to step 2 of this process.
|
||||
|
||||
|
||||
### Step 2: Querying actions
|
||||
|
||||
Key bindings and commands are deserialized by basically storing individual actions to a map.
|
||||
- `KeyMapping` is basically an `IMap<KeyChord, ActionAndArgs>` with a few extra functions. In fact, it actually
|
||||
stores key binding data to a `std::map<KeyChord, ActionAndArgs>` and directly interacts with it.
|
||||
- `Command::LayerJson` populates an `IMap<String, Command>` during deserialization as it iterates over every action.
|
||||
Note that `Command` can be interpreted as a wrapper for `ActionAndArgs` with more stuff here.
|
||||
|
||||
It makes sense to store these actions as maps. So, following step 1 above, we can also store and expose actions
|
||||
something like the following:
|
||||
|
||||
```c++
|
||||
runtimeclass ActionMap
|
||||
{
|
||||
ActionAndArgs GetActionByKeyChord(KeyChord keys);
|
||||
|
||||
KeyChord GetKeyBindingForAction(ShortcutAction action);
|
||||
KeyChord GetKeyBindingForAction(ShortcutAction action, IActionArgs actionArgs);
|
||||
|
||||
IMapView<String, Command> NameMap { get; };
|
||||
|
||||
// Future Considerations:
|
||||
// - [#6899]: Action IDs --> GetActionByID()
|
||||
}
|
||||
```
|
||||
|
||||
The getters will return null if a matching action or key chord is not found. Since iterable commands need to be expanded at in TerminalApp, we'll just expose `NameMap`, then let TerminalApp perform the expansion as they do now. Internally, we can store the actions as follows:
|
||||
|
||||
```c++
|
||||
std::map<KeyChord, InternalActionID> _KeyMap;
|
||||
std::map<InternalActionID, Command> _ActionMap;
|
||||
```
|
||||
|
||||
`InternalActionID` will be a hash of `ActionAndArgs` such that two `ActionAndArgs` with the same `ShortcutAction` and `IActionArgs` output the same hash value.
|
||||
|
||||
`GetActionByKeyChord` will use `_KeyMap` to find the `InternalActionID`, then use the `_ActionMap` to find the bound `Command`.
|
||||
`GetKeyBindingForAction` will hash the provided `ActionAndArgs` (constructed by the given parameters) and check `_ActionMap` for the given `InternalActionID`.
|
||||
|
||||
`NameMap` will need to ensure every action in `_ActionMap` is added to the output name map if it has an associated name. This is done by simply iterating over `_ActionMap`. Nested commands must be added separately because they cannot be hashed.
|
||||
|
||||
`ActionMap` will have an `AddAction(Command cmd)` that will update the internal state whenever a command is registered. If the given command is valid, we will check for collisions and resolve them. Otherwise, we will consider this an "unbound" action and update the internal state normally. It is important that we don't handle "unbound" actions differently because this ensures that we are explicitly unbinding a key chord.
|
||||
|
||||
### Step 3: Settings UI needs
|
||||
|
||||
After the former two steps are completed, the new representation of actions in the settings model is now on-par with
|
||||
what we have today. In order to bind these new actions to the Settings UI, we need the following:
|
||||
|
||||
1. Exposing the maps
|
||||
- `ActionMap::KeyBindings` and `ActionMap::Commands` may need to be added to pass the full list of actions to the Settings UI.
|
||||
- In doing this, we can already update the Settings UI to include a better view of our actions.
|
||||
2. Creating a copy of the settings model
|
||||
- The Settings UI operates by binding the XAML controls to a copy of the settings model.
|
||||
- Copying the `ActionMap` is fairly simple. Just copy the internal state and ensure that `Command::Copy` is called such that no reference to the original WinRT objects persist. Since we are using `InternalActionID`, we will not have to worry about multiple `Command` references existing within the same `ActionMap`.
|
||||
3. Modifying the `Command`s
|
||||
- `ActionMap` must be responsible for changing `Command`s so that we can ensure `ActionMap` always has a correct internal state:
|
||||
- It is important that `Command` only exposes getters (not setters) to ensure `ActionMap` is up to date.
|
||||
- If a key chord is being changed, update the `_KeyMap` and the `Command` itself.
|
||||
- If a key binding is being deleted, add an unbound action to the given key chord.
|
||||
- This is similar to how color schemes are maintained today.
|
||||
- In the event that name/key-chord is set to something that's already taken, we need to propagate those changes to
|
||||
the rest of `ActionMap`. As we do with the JSON, we respect the last name/key-chord set by the user. See [Modifying Actions](#modifying-actions)
|
||||
in potential issues.
|
||||
- For the purposes of the key bindings page, we will introduce a `KeyBindingViewModel` to serve as an intermediator between the settings UI and the settings model. The view model will be responsible for things like...
|
||||
- exposing relevant information to the UI controls
|
||||
- converting UI control interactions into proper API calls to the settings model
|
||||
4. Serialization
|
||||
- `Command::ToJson()` and `ActionMap::ToJson()` should perform most of the work for us. Simply iterate over the `_ActionMap` and call `Command::ToJson` on each action.
|
||||
- See [Unbinding actions](#unbinding-actions) in potential issues.
|
||||
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
N/A
|
||||
|
||||
## Capabilities
|
||||
|
||||
N/A
|
||||
|
||||
### Accessibility
|
||||
|
||||
N/A
|
||||
|
||||
### Security
|
||||
|
||||
N/A
|
||||
|
||||
### Reliability
|
||||
|
||||
N/A
|
||||
|
||||
### Compatibility
|
||||
|
||||
N/A
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
## Potential Issues
|
||||
|
||||
### Layering Actions
|
||||
|
||||
We need a way to determine where an action came from to minimize how many actions we serialize when we
|
||||
write to disk. This is a two part approach that happens as we're loading the settings
|
||||
1. Load defaults.json
|
||||
- For each of the actions in the JSON...
|
||||
- Construct the `Command` (basically the `Command::LayerJson` we have today)
|
||||
- Add it to the `ActionMap`
|
||||
- this should update the internal state of `ActionMap` appropriately
|
||||
- if the newly added key chord conflicts with a pre-existing one,
|
||||
redirect `_KeyMap` to the newly added `Command` instead,
|
||||
and update the conflicting one.
|
||||
2. Load settings.json
|
||||
- Create a child for the `ActionMap`
|
||||
- The purpose of a parent is to continue a search when the current `ActionMap` can't find a `Command` for a query. The parent is intended to be immutable.
|
||||
- Load the actions array like normal into the child (see step 1)
|
||||
|
||||
Introducing a parent mechanism to `ActionMap` allows it to understand where a `Command` came from. This allows us to minimize the number of actions we serialize when we write to disk, as opposed to serializing the entire list of actions.
|
||||
|
||||
`ActionMap` queries will need to check their parent when they cannot find a matching `InternalActionID` in their `_ActionMap`.
|
||||
|
||||
Since `NameMap` is generated upon request, we will need to use a `std::set<InternalActionID>` as we generate the `NameMap`. This will ensure that each `Command` is only added to the `NameMap` once. The name map will be generated as follows:
|
||||
1. Get an accumulated list of `Command`s from our parents
|
||||
2. Iterate over the list...
|
||||
- Update `NameMap` with any new `Command`s (tracked by the `std::set<InternalActionID>`)
|
||||
|
||||
Nested commands will be saved to their own map since they do not have an `InternalActionID`.
|
||||
- During `ActionMap`'s population, we must ensure to resolve any conflicts immediately. This means that any new `Command`s that generate a name conflicting with a nested command will take priority (and we'll remove the nested command from its own map). Conversely, if a new nested command conflicts with an existing standard `Command`, we can ignore it because our generation of `NameMap` will handle it.
|
||||
- When populating `NameMap`, we must first add all of the standard `Command`s. To ensure layering is accomplished correctly, we will need to start from the top-most parent and update `NameMap`. As we go down the inheritance tree, any conflicts are resolved by prioritizing the current layer (the child). This ensures that the current layer takes priority.
|
||||
- After adding all of the standard `Command`s to the `NameMap`, we can then register all of the nested commands. Since nested commands have no identifier other than a name, we cannot use the `InternalActionID` heuristic. However, as mentioned earlier, we are updating our internal nested command map as we add new actions. So when we are generating the name map, we can assume that all of these nested commands now have priority. Thus, we simply add all of these nested commands to the name map. Any conflicts are resolved in favor of th nested command.
|
||||
|
||||
### Modifying Actions
|
||||
|
||||
There are several ways a command can be modified:
|
||||
- change/remove the key chord
|
||||
- change the name
|
||||
- change the icon
|
||||
- change the action
|
||||
|
||||
It is important that these modifications are done through `ActionMap` instead of `Command`.
|
||||
This is to ensure that the `ActionMap` is always aligned with `Command`'s values. `Command` should only expose getters in the projected type to enforce this. Thus, we can add the following functions to `ActionMap`:
|
||||
|
||||
```c++
|
||||
runtimeclass ActionMap
|
||||
{
|
||||
void SetKeyChord(Command cmd, KeyChord keys);
|
||||
void SetName(Command cmd, String name);
|
||||
void SetIcon(Command cmd, String iconPath);
|
||||
void SetAction(Command cmd, ShortcutAction action, IActionArgs actionArgs);
|
||||
}
|
||||
```
|
||||
|
||||
`SetKeyChord` will need to make sure to modify the `_KeyMap` and the provided `Command`.
|
||||
If the new key chord was already taken, we also need to update the conflicting `Command`
|
||||
and remove its key chord.
|
||||
|
||||
`SetName` will need to make sure to modify the `Command` in `_ActionMap` and regenerate `NameMap`.
|
||||
|
||||
`SetIcon` will only need to modify the provided `Command`. We can choose to not expose this in the `ActionMap`, but doing so makes the API consistent.
|
||||
|
||||
`SetAction` will need to begin by updating the provided `Command`'s `ActionAndArgs`.
|
||||
If the generated name is being used, the name will need to be updated. `_ActionMap` will need to be updated with a new `InternalActionID` for the new action. This is a major operation and so all views exposed will need to be regenerated.
|
||||
|
||||
Regarding [Layering Actions](#layering-actions), if the `Command` does not exist in the current layer,
|
||||
but exists in a parent layer, we need to...
|
||||
0. check if it exists
|
||||
- use the hash `InternalActionID` to see if it exists in the current layer
|
||||
- if it doesn't (which is the case we're trying to solve here), call `_GetActionByID(InternalActionID)` to retrieve the `Command` wherever it may be. This helper function simply checks the current layer, if none is found, it recursively checks its parents until a match is found.
|
||||
1. duplicate it with `Command::Copy`
|
||||
2. store the duplicate in the current layer
|
||||
- `ActionMap::AddAction(duplicate)`
|
||||
3. make the modification to the duplicate
|
||||
|
||||
This ensures that the change persists post-serialization.
|
||||
|
||||
TerminalApp has no reason to ever call these setters. To ensure that relationship, we will introduce an `IActionMapView` interface that will only expose `ActionMap` query functions. Conversely, `ActionMap` will be exposed to the TerminalSettingsEditor to allow for modifications.
|
||||
|
||||
### Unbinding actions
|
||||
|
||||
Removing a name is currently omitted from this spec because there
|
||||
is no Settings UI use case for it at the moment. This scenario is
|
||||
designed for Command Palette customization.
|
||||
|
||||
The only kind of unbinding currently in scope is freeing a key chord such that
|
||||
no action is executed on that key stroke. To do this, simply `ActionMap::AddAction` a `Command` with...
|
||||
- `ActionAndArgs`: `ShortcutAction = Invalid` and `IActionArgs = nullptr`
|
||||
- `Keys` being the provided key chord
|
||||
|
||||
In explicitly storing an "unbound" action, we are explicitly saying that this key chord
|
||||
must be passed through and this string must be removed from the command palette. `AddAction` automatically handles updating the internal state of `ActionMap` and any conflicting `Commands`.
|
||||
|
||||
This allows us to output something like this in the JSON:
|
||||
|
||||
```js
|
||||
{ "command": "unbound", "keys": "ctrl+c" }
|
||||
```
|
||||
|
||||
### Consolidated Actions
|
||||
|
||||
`AddAction` must be a bit smarter when it comes to the following scenario:
|
||||
- Given a command that unbinds a key chord: `{ "command": "unbound", "keys": "ctrl+c" }`
|
||||
- And... that key chord was set in a parent layer `{ "command": "copy", "keys": "ctrl+c" }`
|
||||
- But... the action has another key chord from a parent layer `{ "command": "copy", "keys": "ctrl+shift+c" }`
|
||||
|
||||
`_ActionMap` does not contain any information about a parent layer; it only contains actions introduced in the current layer. Thus, in the scenario above, unbinding `ctrl+c` is what is relevant to `_ActionMap`. However, this may cause some complications for `GetKeyChordForAction`. We cannot simply check our internal `_ActionMap`, because the primary key chord in the entry may be incorrect. Again, this is because `_ActionMap` is only aware of what was bound in the current layer.
|
||||
|
||||
To get around this issue, we've introduced `_ConsolidatedActions`. In a way, `_ConsolidatedActions` is similar to `_ActionMap`, except that it consolidates the `Command` data into one entry constructed across the current layer and the parent layers. Specifically, in the scenario above, `_ActionMap` will say that `copy` has no key chords. In fact, `_ActionMap` has no reason to have `copy` at all, because it was not introduced in this layer. Conversely, `_ConsolidatedActions` holds `copy` with a `ctrl+shift+c` binding, which is then returned to `GetKeyChordForAction`.
|
||||
|
||||
To maintain `_ConsolidatedActions`, any new action added to the Action Map must also update `_ConsolidatedActions`. It is especially important to handle and propagate collisions to `_ConsolidatedActions`.
|
||||
|
||||
When querying Action Map for an ID, we should always check in the following order:
|
||||
- `_ConsolidatedActions`
|
||||
- `_ActionMap`
|
||||
- repeat this process for each parent
|
||||
|
||||
This is to ensure that we are returning the correct and wholistic view of a `Command` on a query. Rather than acquiring a `Command` constructed in this layer, we receive one that contains all of the data acquired across the entire Action Map and its parents.
|
||||
|
||||
## Future considerations
|
||||
|
||||
There are a number of ideas regarding actions that would be fairly trivial to implement given this refactor:
|
||||
- [#6899]: Action IDs
|
||||
- As actions grow to become more widespread within Windows Terminal (i.e. dropdown and jumplist integration),
|
||||
a formal ID system would help users reference the same action throughout the app. With the internal
|
||||
ID system introduced earlier, we would simply introduce a new
|
||||
`std:map<string, InternalActionID> _ExternalIDMap` that is updated like the others, and add a `String ID`
|
||||
property to `Action`.
|
||||
- [#8100] Source Tracking
|
||||
- Identifying where a setting came from can be very beneficial in the settings model and UI. For example,
|
||||
profile settings now have an `OverrideSource` getter that describes what `Profile` object the setting
|
||||
came from (i.e. base layer, profile generator, etc...). A similar system can be used for `Action` in
|
||||
that we record if the action was last modified in defaults.json or settings.json.
|
||||
- There seems to be no desire for action inheritance (i.e. inheriting the name/key-chord from the parent).
|
||||
So this should be sufficient.
|
||||
|
||||
## Resources
|
||||
|
||||
[#885]: https://github.com/microsoft/terminal/issues/885
|
||||
[#6899]: https://github.com/microsoft/terminal/issues/6899
|
||||
[#8100]: https://github.com/microsoft/terminal/issues/8100
|
||||
[#8767]: https://github.com/microsoft/terminal/issues/8767
|
||||
|
||||
Other references:
|
||||
[Settings UI: Actions Page]: https://github.com/microsoft/terminal/issues/6900
|
||||
|
||||
[Settings UI: Actions Page Design]: https://github.com/microsoft/terminal/pulls/9427
|
||||
|
||||
[Action ID Spec]: https://github.com/microsoft/terminal/issues/7175
|
|
@ -23,7 +23,8 @@
|
|||
<ProjectGuid>{96274800-9574-423E-892A-909FBE2AC8BE}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>EchoCon</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace GUIConsole.ConPTY
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the psuedoconsole and run the process as shown in
|
||||
/// Start the pseudoconsole and run the process as shown in
|
||||
/// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-pseudoconsole
|
||||
/// </summary>
|
||||
/// <param name="command">the command to run, e.g. cmd.exe</param>
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace MiniTerm
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the psuedoconsole and run the process as shown in
|
||||
/// Start the pseudoconsole and run the process as shown in
|
||||
/// https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-pseudoconsole
|
||||
/// </summary>
|
||||
/// <param name="command">the command to run, e.g. cmd.exe</param>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<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">
|
||||
|
@ -21,6 +22,7 @@
|
|||
<EventProviderId Value="EventProvider_TerminalApp" />
|
||||
<EventProviderId Value="EventProvider_TerminalWin32Host" />
|
||||
<EventProviderId Value="EventProvider_TerminalRemoting" />
|
||||
<EventProviderId Value="EventProvider_TerminalDirectX" />
|
||||
</EventProviders>
|
||||
</EventCollectorId>
|
||||
</Collectors>
|
||||
|
|
|
@ -11,18 +11,8 @@
|
|||
// - 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;
|
||||
}
|
||||
ATTR_ROW::ATTR_ROW(const uint16_t width, const TextAttribute attr) :
|
||||
_data(width, attr) {}
|
||||
|
||||
// Routine Description:
|
||||
// - Sets all properties of the ATTR_ROW to default values
|
||||
|
@ -30,8 +20,7 @@ ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr) noexcept
|
|||
// - 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));
|
||||
_data.replace(0, _data.size(), attr);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -43,48 +32,9 @@ void ATTR_ROW::Reset(const TextAttribute attr)
|
|||
// - newWidth - The new width of the row.
|
||||
// Return Value:
|
||||
// - <none>, throws exceptions on failures.
|
||||
void ATTR_ROW::Resize(const size_t newWidth)
|
||||
void ATTR_ROW::Resize(const uint16_t newWidth)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, 0 == newWidth);
|
||||
|
||||
// Easy case. If the new row is longer, increase the length of the last run by how much new space there is.
|
||||
if (newWidth > _cchRowWidth)
|
||||
{
|
||||
// Get the attribute that covers the final column of old width.
|
||||
const auto runPos = FindAttrIndex(_cchRowWidth - 1, nullptr);
|
||||
auto& run = _list.at(runPos);
|
||||
|
||||
// Extend its length by the additional columns we're adding.
|
||||
run.SetLength(run.GetLength() + newWidth - _cchRowWidth);
|
||||
|
||||
// Store that the new total width we represent is the new width.
|
||||
_cchRowWidth = newWidth;
|
||||
}
|
||||
// harder case: new row is shorter.
|
||||
else
|
||||
{
|
||||
// Get the attribute that covers the final column of the new width
|
||||
size_t CountOfAttr = 0;
|
||||
const auto runPos = FindAttrIndex(newWidth - 1, &CountOfAttr);
|
||||
auto& run = _list.at(runPos);
|
||||
|
||||
// CountOfAttr was given to us as "how many columns left from this point forward are covered by the returned run"
|
||||
// So if the original run was B5 covering a 5 size OldWidth and we have a NewWidth of 3
|
||||
// then when we called FindAttrIndex, it returned the B5 as the pIndexedRun and a 2 for how many more segments it covers
|
||||
// after and including the 3rd column.
|
||||
// B5-2 = B3, which is what we desire to cover the new 3 size buffer.
|
||||
run.SetLength(run.GetLength() - CountOfAttr + 1);
|
||||
|
||||
// Store that the new total width we represent is the new width.
|
||||
_cchRowWidth = newWidth;
|
||||
|
||||
// Erase segments after the one we just updated.
|
||||
_list.erase(_list.cbegin() + runPos + 1, _list.cend());
|
||||
|
||||
// NOTE: Under some circumstances here, we have leftover run segments in memory or blank run segments
|
||||
// in memory. We're not going to waste time redimensioning the array in the heap. We're just noting that the useful
|
||||
// portions of it have changed.
|
||||
}
|
||||
_data.resize_trailing_extent(newWidth);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -95,104 +45,23 @@ void ATTR_ROW::Resize(const size_t newWidth)
|
|||
// - the text attribute at column
|
||||
// Note:
|
||||
// - will throw on error
|
||||
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column) const
|
||||
TextAttribute ATTR_ROW::GetAttrByColumn(const uint16_t column) const
|
||||
{
|
||||
return GetAttrByColumn(column, nullptr);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - returns a copy of the TextAttribute at the specified column
|
||||
// Arguments:
|
||||
// - column - the column to get the attribute for
|
||||
// - pApplies - if given, fills how long this attribute will apply for
|
||||
// Return Value:
|
||||
// - the text attribute at column
|
||||
// Note:
|
||||
// - will throw on error
|
||||
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
|
||||
size_t* const pApplies) const
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, column >= _cchRowWidth);
|
||||
const auto runPos = FindAttrIndex(column, pApplies);
|
||||
return _list.at(runPos).GetAttributes();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - reports how many runs we have stored (to be used for some optimizations
|
||||
// Return Value:
|
||||
// - Count of runs. 1 means we have 1 color to represent the entire row.
|
||||
size_t ATTR_ROW::GetNumberOfRuns() const noexcept
|
||||
{
|
||||
return _list.size();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - This routine finds the nth attribute in this ATTR_ROW.
|
||||
// Arguments:
|
||||
// - index - which attribute to find
|
||||
// - applies - on output, contains corrected length of indexed attr.
|
||||
// for example, if the attribute string was { 5, BLUE } and the requested
|
||||
// index was 3, CountOfAttr would be 2.
|
||||
// Return Value:
|
||||
// - const reference to attribute run object
|
||||
size_t ATTR_ROW::FindAttrIndex(const size_t index, size_t* const pApplies) const
|
||||
{
|
||||
FAIL_FAST_IF(!(index < _cchRowWidth)); // The requested index cannot be longer than the total length described by this set of Attrs.
|
||||
|
||||
size_t cTotalLength = 0;
|
||||
|
||||
FAIL_FAST_IF(!(_list.size() > 0)); // There should be a non-zero and positive number of items in the array.
|
||||
|
||||
// Scan through the internal array from position 0 adding up the lengths that each attribute applies to
|
||||
auto runPos = _list.cbegin();
|
||||
do
|
||||
{
|
||||
cTotalLength += runPos->GetLength();
|
||||
|
||||
if (cTotalLength > index)
|
||||
{
|
||||
// If we've just passed up the requested index with the length we added, break early
|
||||
break;
|
||||
}
|
||||
|
||||
runPos++;
|
||||
} while (runPos < _list.cend());
|
||||
|
||||
// we should have broken before falling out the while case.
|
||||
// if we didn't break, then this ATTR_ROW wasn't filled with enough attributes for the entire row of characters
|
||||
FAIL_FAST_IF(runPos >= _list.cend());
|
||||
|
||||
// The remaining iterator position is the position of the attribute that is applicable at the position requested (index)
|
||||
// Calculate its remaining applicability if requested
|
||||
|
||||
// The length on which the found attribute applies is the total length seen so far minus the index we were searching for.
|
||||
FAIL_FAST_IF(!(cTotalLength > index)); // The length of all attributes we counted up so far should be longer than the index requested or we'll underflow.
|
||||
|
||||
if (nullptr != pApplies)
|
||||
{
|
||||
const auto attrApplies = cTotalLength - index;
|
||||
FAIL_FAST_IF(!(attrApplies > 0)); // An attribute applies for >0 characters
|
||||
// MSFT: 17130145 - will restore this and add a better assert to catch the real issue.
|
||||
//FAIL_FAST_IF(!(attrApplies <= _cchRowWidth)); // An attribute applies for a maximum of the total length available to us
|
||||
|
||||
*pApplies = attrApplies;
|
||||
}
|
||||
|
||||
return runPos - _list.cbegin();
|
||||
return _data.at(column);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Finds the hyperlink IDs present in this row and returns them
|
||||
// Return value:
|
||||
// - The hyperlink IDs present in this row
|
||||
std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
|
||||
std::vector<uint16_t> ATTR_ROW::GetHyperlinks() const
|
||||
{
|
||||
std::vector<uint16_t> ids;
|
||||
for (const auto& run : _list)
|
||||
for (const auto& run : _data.runs())
|
||||
{
|
||||
if (run.GetAttributes().IsHyperlink())
|
||||
if (run.value.IsHyperlink())
|
||||
{
|
||||
ids.emplace_back(run.GetAttributes().GetHyperlinkId());
|
||||
ids.emplace_back(run.value.GetHyperlinkId());
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
|
@ -205,12 +74,10 @@ std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
|
|||
// - attr - Attribute (color) to fill remaining characters with
|
||||
// Return Value:
|
||||
// - <none>
|
||||
bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
|
||||
bool ATTR_ROW::SetAttrToEnd(const uint16_t beginIndex, const TextAttribute attr)
|
||||
{
|
||||
size_t const length = _cchRowWidth - iStart;
|
||||
|
||||
const TextAttributeRun run(length, attr);
|
||||
return SUCCEEDED(InsertAttrRuns({ &run, 1 }, iStart, _cchRowWidth - 1, _cchRowWidth));
|
||||
_data.replace(gsl::narrow<uint16_t>(beginIndex), _data.size(), attr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -221,419 +88,47 @@ bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
|
|||
// - replaceWith - the new value for the matching runs' attributes.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept
|
||||
void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith)
|
||||
{
|
||||
for (auto& run : _list)
|
||||
{
|
||||
if (run.GetAttributes() == toBeReplacedAttr)
|
||||
{
|
||||
run.SetAttributes(replaceWith);
|
||||
}
|
||||
}
|
||||
_data.replace_values(toBeReplacedAttr, replaceWith);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Takes a array of attribute runs, and inserts them into this row from startIndex to endIndex.
|
||||
// - For example, if the current row was was [{4, BLUE}], the merge string
|
||||
// was [{ 2, RED }], with (StartIndex, EndIndex) = (1, 2),
|
||||
// then the row would modified to be = [{ 1, BLUE}, {2, RED}, {1, BLUE}].
|
||||
// - Takes an attribute, and merges it into this row from beginIndex (inclusive) to endIndex (exclusive).
|
||||
// - For example, if the current row was was [{4, BLUE}], the merge arguments were
|
||||
// { beginIndex = 1, endIndex = 3, newAttr = RED }, then the row would modified to be
|
||||
// [{ 1, BLUE}, {2, RED}, {1, BLUE}].
|
||||
// Arguments:
|
||||
// - rgInsertAttrs - The array of attrRuns to merge into this row.
|
||||
// - cInsertAttrs - The number of elements in rgInsertAttrs
|
||||
// - iStart - The index in the row to place the array of runs.
|
||||
// - iEnd - the final index of the merge runs
|
||||
// - BufferWidth - the width of the row.
|
||||
// - beginIndex, endIndex: The [beginIndex, endIndex) range that's to be replaced with newAttr.
|
||||
// - newAttr: The attribute to merge into this row.
|
||||
// Return Value:
|
||||
// - STATUS_NO_MEMORY if there wasn't enough memory to insert the runs
|
||||
// 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)
|
||||
// - <none>
|
||||
void ATTR_ROW::Replace(const uint16_t beginIndex, const uint16_t endIndex, const TextAttribute& newAttr)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
_data.replace(beginIndex, endIndex, newAttr);
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::begin() const noexcept
|
||||
{
|
||||
return AttrRowIterator(this);
|
||||
return _data.begin();
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::end() const noexcept
|
||||
{
|
||||
return AttrRowIterator::CreateEndIterator(this);
|
||||
return _data.end();
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::cbegin() const noexcept
|
||||
{
|
||||
return AttrRowIterator(this);
|
||||
return _data.cbegin();
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::cend() const noexcept
|
||||
{
|
||||
return AttrRowIterator::CreateEndIterator(this);
|
||||
return _data.cend();
|
||||
}
|
||||
|
||||
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);
|
||||
return a._data == b._data;
|
||||
}
|
||||
|
|
|
@ -20,16 +20,17 @@ Revision History:
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "TextAttributeRun.hpp"
|
||||
#include "AttrRowIterator.hpp"
|
||||
#include "til/rle.h"
|
||||
#include "TextAttribute.hpp"
|
||||
|
||||
class ATTR_ROW final
|
||||
{
|
||||
public:
|
||||
using const_iterator = typename AttrRowIterator;
|
||||
using rle_vector = til::small_rle<TextAttribute, uint16_t, 1>;
|
||||
|
||||
ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
|
||||
noexcept;
|
||||
public:
|
||||
using const_iterator = rle_vector::const_iterator;
|
||||
|
||||
ATTR_ROW(uint16_t width, TextAttribute attr);
|
||||
|
||||
~ATTR_ROW() = default;
|
||||
|
||||
|
@ -39,28 +40,13 @@ public:
|
|||
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;
|
||||
TextAttribute GetAttrByColumn(uint16_t column) const;
|
||||
std::vector<uint16_t> GetHyperlinks() const;
|
||||
|
||||
size_t GetNumberOfRuns() const noexcept;
|
||||
|
||||
size_t FindAttrIndex(const size_t index,
|
||||
size_t* const pApplies) const;
|
||||
|
||||
std::vector<uint16_t> GetHyperlinks();
|
||||
|
||||
bool SetAttrToEnd(const UINT iStart, const TextAttribute attr);
|
||||
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept;
|
||||
|
||||
void Resize(const size_t newWidth);
|
||||
|
||||
[[nodiscard]] HRESULT InsertAttrRuns(const gsl::span<const TextAttributeRun> newAttrs,
|
||||
const size_t iStart,
|
||||
const size_t iEnd,
|
||||
const size_t cBufferWidth);
|
||||
|
||||
static std::vector<TextAttributeRun> PackAttrs(const std::vector<TextAttribute>& attrs);
|
||||
bool SetAttrToEnd(uint16_t beginIndex, TextAttribute attr);
|
||||
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith);
|
||||
void Resize(uint16_t newWidth);
|
||||
void Replace(uint16_t beginIndex, uint16_t endIndex, const TextAttribute& newAttr);
|
||||
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
|
@ -69,17 +55,14 @@ public:
|
|||
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;
|
||||
rle_vector _data;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class AttrRowTests;
|
||||
friend class CommonState;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "AttrRowIterator.hpp"
|
||||
#include "AttrRow.hpp"
|
||||
|
||||
AttrRowIterator AttrRowIterator::CreateEndIterator(const ATTR_ROW* const attrRow) noexcept
|
||||
{
|
||||
AttrRowIterator it{ attrRow };
|
||||
it._setToEnd();
|
||||
return it;
|
||||
}
|
||||
|
||||
AttrRowIterator::AttrRowIterator(const ATTR_ROW* const attrRow) noexcept :
|
||||
_pAttrRow{ attrRow },
|
||||
_run{ attrRow->_list.cbegin() },
|
||||
_currentAttributeIndex{ 0 },
|
||||
_exceeded{ false }
|
||||
{
|
||||
}
|
||||
|
||||
AttrRowIterator::operator bool() const noexcept
|
||||
{
|
||||
return !_exceeded && _run < _pAttrRow->_list.cend();
|
||||
}
|
||||
|
||||
bool AttrRowIterator::operator==(const AttrRowIterator& it) const noexcept
|
||||
{
|
||||
return (_pAttrRow == it._pAttrRow &&
|
||||
_run == it._run &&
|
||||
_currentAttributeIndex == it._currentAttributeIndex &&
|
||||
_exceeded == it._exceeded);
|
||||
}
|
||||
|
||||
bool AttrRowIterator::operator!=(const AttrRowIterator& it) const noexcept
|
||||
{
|
||||
return !(*this == it);
|
||||
}
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
if (!_exceeded)
|
||||
{
|
||||
if (movement >= 0)
|
||||
{
|
||||
_increment(gsl::narrow<size_t>(movement));
|
||||
}
|
||||
else
|
||||
{
|
||||
_decrement(gsl::narrow<size_t>(-movement));
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator-=(const ptrdiff_t& movement)
|
||||
{
|
||||
return this->operator+=(-movement);
|
||||
}
|
||||
|
||||
const TextAttribute* AttrRowIterator::operator->() const
|
||||
{
|
||||
THROW_HR_IF(E_BOUNDS, _exceeded);
|
||||
return &_run->GetAttributes();
|
||||
}
|
||||
|
||||
const TextAttribute& AttrRowIterator::operator*() const
|
||||
{
|
||||
THROW_HR_IF(E_BOUNDS, _exceeded);
|
||||
return _run->GetAttributes();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - increments the index the iterator points to
|
||||
// Arguments:
|
||||
// - count - the amount to increment by
|
||||
void AttrRowIterator::_increment(size_t count) noexcept
|
||||
{
|
||||
while (count > 0)
|
||||
{
|
||||
const size_t runLength = _run->GetLength();
|
||||
if (count + _currentAttributeIndex < runLength)
|
||||
{
|
||||
_currentAttributeIndex += count;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
count -= runLength - _currentAttributeIndex;
|
||||
++_run;
|
||||
_currentAttributeIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - decrements the index the iterator points to
|
||||
// Arguments:
|
||||
// - count - the amount to decrement by
|
||||
void AttrRowIterator::_decrement(size_t count) noexcept
|
||||
{
|
||||
while (count > 0)
|
||||
{
|
||||
// If there's still space within this color attribute to move left, do so.
|
||||
if (count <= _currentAttributeIndex)
|
||||
{
|
||||
_currentAttributeIndex -= count;
|
||||
return;
|
||||
}
|
||||
// If there's not space, move to the previous attribute run
|
||||
// We'll walk through above on the if branch to move left further (if necessary)
|
||||
else
|
||||
{
|
||||
// make sure we don't go out of bounds
|
||||
if (_run == _pAttrRow->_list.cbegin())
|
||||
{
|
||||
_exceeded = true;
|
||||
return;
|
||||
}
|
||||
count -= _currentAttributeIndex + 1;
|
||||
--_run;
|
||||
_currentAttributeIndex = _run->GetLength() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - sets fields on the iterator to describe the end() state of the ATTR_ROW
|
||||
void AttrRowIterator::_setToEnd() noexcept
|
||||
{
|
||||
_run = _pAttrRow->_list.cend();
|
||||
_currentAttributeIndex = 0;
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- AttrRowIterator.hpp
|
||||
|
||||
Abstract:
|
||||
- iterator for ATTR_ROW to walk the TextAttributes of the run
|
||||
- read only iterator
|
||||
|
||||
Author(s):
|
||||
- Austin Diviness (AustDi) 04-Jun-2018
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TextAttribute.hpp"
|
||||
#include "TextAttributeRun.hpp"
|
||||
|
||||
class ATTR_ROW;
|
||||
|
||||
class AttrRowIterator final
|
||||
{
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = TextAttribute;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = TextAttribute*;
|
||||
using reference = TextAttribute&;
|
||||
|
||||
static AttrRowIterator CreateEndIterator(const ATTR_ROW* const attrRow) noexcept;
|
||||
|
||||
AttrRowIterator(const ATTR_ROW* const attrRow) noexcept;
|
||||
|
||||
operator bool() const noexcept;
|
||||
|
||||
bool operator==(const AttrRowIterator& it) const noexcept;
|
||||
bool operator!=(const AttrRowIterator& it) const noexcept;
|
||||
|
||||
AttrRowIterator& operator++() noexcept
|
||||
{
|
||||
_increment(1);
|
||||
return *this;
|
||||
}
|
||||
AttrRowIterator operator++(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_increment(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
AttrRowIterator& operator+=(const ptrdiff_t& movement);
|
||||
AttrRowIterator& operator-=(const ptrdiff_t& movement);
|
||||
|
||||
AttrRowIterator& operator--() noexcept
|
||||
{
|
||||
_decrement(1);
|
||||
return *this;
|
||||
}
|
||||
AttrRowIterator operator--(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_decrement(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
const TextAttribute* operator->() const;
|
||||
const TextAttribute& operator*() const;
|
||||
|
||||
private:
|
||||
boost::container::small_vector_base<TextAttributeRun>::const_iterator _run;
|
||||
const ATTR_ROW* _pAttrRow;
|
||||
size_t _currentAttributeIndex; // index of TextAttribute within the current TextAttributeRun
|
||||
bool _exceeded;
|
||||
|
||||
void _increment(size_t count) noexcept;
|
||||
void _decrement(size_t count) noexcept;
|
||||
void _setToEnd() noexcept;
|
||||
};
|
|
@ -16,7 +16,7 @@
|
|||
// - pParent - the text buffer that this row belongs to
|
||||
// Return Value:
|
||||
// - constructed object
|
||||
ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) noexcept :
|
||||
ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) :
|
||||
_id{ rowId },
|
||||
_rowWidth{ rowWidth },
|
||||
_charRow{ rowWidth, this },
|
||||
|
@ -107,103 +107,91 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
|
|||
{
|
||||
THROW_HR_IF(E_INVALIDARG, index >= _charRow.size());
|
||||
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= _charRow.size());
|
||||
size_t currentIndex = index;
|
||||
|
||||
// If we're given a right-side column limit, use it. Otherwise, the write limit is the final column index available in the char row.
|
||||
const auto finalColumnInRow = limitRight.value_or(_charRow.size() - 1);
|
||||
|
||||
if (it)
|
||||
auto currentColor = it->TextAttr();
|
||||
uint16_t colorUses = 0;
|
||||
uint16_t colorStarts = gsl::narrow_cast<uint16_t>(index);
|
||||
uint16_t currentIndex = colorStarts;
|
||||
|
||||
while (it && currentIndex <= finalColumnInRow)
|
||||
{
|
||||
// Accumulate usages of the same color so we can spend less time in InsertAttrRuns rewriting it.
|
||||
auto currentColor = it->TextAttr();
|
||||
size_t colorUses = 0;
|
||||
size_t colorStarts = index;
|
||||
|
||||
while (it && currentIndex <= finalColumnInRow)
|
||||
// Fill the color if the behavior isn't set to keeping the current color.
|
||||
if (it->TextAttrBehavior() != TextAttributeBehavior::Current)
|
||||
{
|
||||
// Fill the color if the behavior isn't set to keeping the current color.
|
||||
if (it->TextAttrBehavior() != TextAttributeBehavior::Current)
|
||||
// If the color of this cell is the same as the run we're currently on,
|
||||
// just increment the counter.
|
||||
if (currentColor == it->TextAttr())
|
||||
{
|
||||
// If the color of this cell is the same as the run we're currently on,
|
||||
// just increment the counter.
|
||||
if (currentColor == it->TextAttr())
|
||||
{
|
||||
++colorUses;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, commit this color into the run and save off the new one.
|
||||
const TextAttributeRun run{ colorUses, currentColor };
|
||||
// Now commit the new color runs into the attr row.
|
||||
LOG_IF_FAILED(_attrRow.InsertAttrRuns({ &run, 1 },
|
||||
colorStarts,
|
||||
currentIndex - 1,
|
||||
_charRow.size()));
|
||||
currentColor = it->TextAttr();
|
||||
colorUses = 1;
|
||||
colorStarts = currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the text if the behavior isn't set to saying there's only a color stored in this iterator.
|
||||
if (it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly)
|
||||
{
|
||||
const bool fillingLastColumn = currentIndex == finalColumnInRow;
|
||||
|
||||
// TODO: MSFT: 19452170 - We need to ensure when writing any trailing byte that the one to the left
|
||||
// is a matching leading byte. Likewise, if we're writing a leading byte, we need to make sure we still have space in this loop
|
||||
// for the trailing byte coming up before writing it.
|
||||
|
||||
// If we're trying to fill the first cell with a trailing byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll advance the index and try again with this value on the next round through the loop.
|
||||
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
}
|
||||
// If we're trying to fill the last cell with a leading byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll exit because we couldn't write a lead at the end of a line.
|
||||
else if (fillingLastColumn && it->DbcsAttr().IsLeading())
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
SetDoubleBytePadded(true);
|
||||
}
|
||||
// Otherwise, copy the data given and increment the iterator.
|
||||
else
|
||||
{
|
||||
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
|
||||
_charRow.GlyphAt(currentIndex) = it->Chars();
|
||||
++it;
|
||||
}
|
||||
|
||||
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
|
||||
// NOTE:
|
||||
// - wrap = std::nullopt --> don't change the wrap value
|
||||
// - wrap = true --> we're filling cells as a steam, consider this a wrap
|
||||
// - wrap = false --> we're filling cells as a block, unwrap
|
||||
if (wrap.has_value() && fillingLastColumn)
|
||||
{
|
||||
// set wrap status on the row to parameter's value.
|
||||
SetWrapForced(*wrap);
|
||||
}
|
||||
++colorUses;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, commit this color into the run and save off the new one.
|
||||
// Now commit the new color runs into the attr row.
|
||||
_attrRow.Replace(colorStarts, currentIndex, currentColor);
|
||||
currentColor = it->TextAttr();
|
||||
colorUses = 1;
|
||||
colorStarts = currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the text if the behavior isn't set to saying there's only a color stored in this iterator.
|
||||
if (it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly)
|
||||
{
|
||||
const bool fillingLastColumn = currentIndex == finalColumnInRow;
|
||||
|
||||
// TODO: MSFT: 19452170 - We need to ensure when writing any trailing byte that the one to the left
|
||||
// is a matching leading byte. Likewise, if we're writing a leading byte, we need to make sure we still have space in this loop
|
||||
// for the trailing byte coming up before writing it.
|
||||
|
||||
// If we're trying to fill the first cell with a trailing byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll advance the index and try again with this value on the next round through the loop.
|
||||
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
}
|
||||
// If we're trying to fill the last cell with a leading byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll exit because we couldn't write a lead at the end of a line.
|
||||
else if (fillingLastColumn && it->DbcsAttr().IsLeading())
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
SetDoubleBytePadded(true);
|
||||
}
|
||||
// Otherwise, copy the data given and increment the iterator.
|
||||
else
|
||||
{
|
||||
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
|
||||
_charRow.GlyphAt(currentIndex) = it->Chars();
|
||||
++it;
|
||||
}
|
||||
|
||||
// Move to the next cell for the next time through the loop.
|
||||
++currentIndex;
|
||||
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
|
||||
// NOTE:
|
||||
// - wrap = std::nullopt --> don't change the wrap value
|
||||
// - wrap = true --> we're filling cells as a steam, consider this a wrap
|
||||
// - wrap = false --> we're filling cells as a block, unwrap
|
||||
if (wrap.has_value() && fillingLastColumn)
|
||||
{
|
||||
// set wrap status on the row to parameter's value.
|
||||
SetWrapForced(*wrap);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
|
||||
// Now commit the final color into the attr row
|
||||
if (colorUses)
|
||||
{
|
||||
const TextAttributeRun run{ colorUses, currentColor };
|
||||
LOG_IF_FAILED(_attrRow.InsertAttrRuns({ &run, 1 },
|
||||
colorStarts,
|
||||
currentIndex - 1,
|
||||
_charRow.size()));
|
||||
}
|
||||
// Move to the next cell for the next time through the loop.
|
||||
++currentIndex;
|
||||
}
|
||||
|
||||
// Now commit the final color into the attr row
|
||||
if (colorUses)
|
||||
{
|
||||
_attrRow.Replace(colorStarts, currentIndex, currentColor);
|
||||
}
|
||||
|
||||
return it;
|
||||
|
|
|
@ -32,8 +32,7 @@ class TextBuffer;
|
|||
class ROW final
|
||||
{
|
||||
public:
|
||||
ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent)
|
||||
noexcept;
|
||||
ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent);
|
||||
|
||||
size_t size() const noexcept { return _rowWidth; }
|
||||
|
||||
|
|
|
@ -5,6 +5,13 @@
|
|||
#include "TextAttribute.hpp"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
|
||||
// Keeping TextColor compact helps us keeping TextAttribute compact,
|
||||
// which in turn ensures that our buffer memory usage is low.
|
||||
static_assert(sizeof(TextAttribute) == 14);
|
||||
static_assert(alignof(TextAttribute) == 2);
|
||||
// Ensure that we can memcpy() and memmove() the struct for performance.
|
||||
static_assert(std::is_trivially_copyable_v<TextAttribute>);
|
||||
|
||||
BYTE TextAttribute::s_legacyDefaultForeground = 7;
|
||||
BYTE TextAttribute::s_legacyDefaultBackground = 0;
|
||||
|
||||
|
|
|
@ -27,8 +27,6 @@ Revision History:
|
|||
#include "WexTestClass.h"
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
class TextAttribute final
|
||||
{
|
||||
public:
|
||||
|
@ -176,12 +174,11 @@ private:
|
|||
static BYTE s_legacyDefaultForeground;
|
||||
static BYTE s_legacyDefaultBackground;
|
||||
|
||||
WORD _wAttrLegacy;
|
||||
TextColor _foreground;
|
||||
TextColor _background;
|
||||
ExtendedAttributes _extendedAttrs;
|
||||
|
||||
uint16_t _hyperlinkId;
|
||||
uint16_t _wAttrLegacy; // sizeof: 2, alignof: 2
|
||||
uint16_t _hyperlinkId; // sizeof: 2, alignof: 2
|
||||
TextColor _foreground; // sizeof: 4, alignof: 1
|
||||
TextColor _background; // sizeof: 4, alignof: 1
|
||||
ExtendedAttributes _extendedAttrs; // sizeof: 1, alignof: 1
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
|
@ -191,13 +188,6 @@ private:
|
|||
#endif
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
// 2 for _wAttrLegacy
|
||||
// 4 for _foreground
|
||||
// 4 for _background
|
||||
// 1 for _extendedAttrs
|
||||
static_assert(sizeof(TextAttribute) <= 13 * sizeof(BYTE), "We should only need 13B for an entire TextAttribute. We may need to increment this in the future as we add additional attributes");
|
||||
|
||||
enum class TextAttributeBehavior
|
||||
{
|
||||
Stored, // use contained text attribute
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- TextAttributeRun.hpp
|
||||
|
||||
Abstract:
|
||||
- contains data structure for run-length-encoding of text attribute data
|
||||
|
||||
Author(s):
|
||||
- Michael Niksa (miniksa) 10-Apr-2014
|
||||
- Paul Campbell (paulcam) 10-Apr-2014
|
||||
|
||||
Revision History:
|
||||
- From components of output.h/.c
|
||||
by Therese Stowell (ThereseS) 1990-1991
|
||||
- Pulled into its own file from textBuffer.hpp/cpp (AustDi, 2017)
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TextAttribute.hpp"
|
||||
|
||||
class TextAttributeRun final
|
||||
{
|
||||
public:
|
||||
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 _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 _attributes; }
|
||||
void SetAttributes(const TextAttribute textAttribute) noexcept { _attributes = textAttribute; }
|
||||
|
||||
private:
|
||||
unsigned int _cchLength{ 0 };
|
||||
TextAttribute _attributes{ 0 };
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class AttrRowTests;
|
||||
#endif
|
||||
};
|
|
@ -37,8 +37,6 @@ Revision History:
|
|||
#include "WexTestClass.h"
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
enum class ColorType : BYTE
|
||||
{
|
||||
IsIndex256 = 0x0,
|
||||
|
@ -102,13 +100,13 @@ public:
|
|||
COLORREF GetRGB() const noexcept;
|
||||
|
||||
private:
|
||||
ColorType _meta : 2;
|
||||
union
|
||||
{
|
||||
BYTE _red, _index;
|
||||
};
|
||||
BYTE _green;
|
||||
BYTE _blue;
|
||||
ColorType _meta;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
|
@ -117,8 +115,6 @@ private:
|
|||
#endif
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
bool constexpr operator==(const TextColor& a, const TextColor& b) noexcept
|
||||
{
|
||||
return a._meta == b._meta &&
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\AttrRow.cpp" />
|
||||
<ClCompile Include="..\AttrRowIterator.cpp" />
|
||||
<ClCompile Include="..\cursor.cpp" />
|
||||
<ClCompile Include="..\OutputCell.cpp" />
|
||||
<ClCompile Include="..\OutputCellIterator.cpp" />
|
||||
|
@ -34,7 +33,6 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\AttrRow.hpp" />
|
||||
<ClInclude Include="..\AttrRowIterator.hpp" />
|
||||
<ClInclude Include="..\cursor.h" />
|
||||
<ClInclude Include="..\DbcsAttribute.hpp" />
|
||||
<ClInclude Include="..\ICharRow.hpp" />
|
||||
|
@ -47,7 +45,6 @@
|
|||
<ClInclude Include="..\search.h" />
|
||||
<ClInclude Include="..\TextColor.h" />
|
||||
<ClInclude Include="..\TextAttribute.h" />
|
||||
<ClInclude Include="..\TextAttributeRun.h" />
|
||||
<ClInclude Include="..\textBuffer.hpp" />
|
||||
<ClInclude Include="..\textBufferCellIterator.hpp" />
|
||||
<ClInclude Include="..\textBufferTextIterator.hpp" />
|
||||
|
|
|
@ -850,7 +850,7 @@ bool TextBuffer::IsDoubleWidthLine(const size_t row) const
|
|||
SHORT TextBuffer::GetLineWidth(const size_t row) const
|
||||
{
|
||||
// Use shift right to quickly divide the width by 2 for double width lines.
|
||||
const auto scale = IsDoubleWidthLine(row) ? 1 : 0;
|
||||
const SHORT scale = IsDoubleWidthLine(row) ? 1 : 0;
|
||||
return GetSize().Width() >> scale;
|
||||
}
|
||||
|
||||
|
@ -863,14 +863,14 @@ COORD TextBuffer::ClampPositionWithinLine(const COORD position) const
|
|||
COORD TextBuffer::ScreenToBufferPosition(const COORD position) const
|
||||
{
|
||||
// Use shift right to quickly divide the X pos by 2 for double width lines.
|
||||
const auto scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
|
||||
const SHORT scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
|
||||
return { position.X >> scale, position.Y };
|
||||
}
|
||||
|
||||
COORD TextBuffer::BufferToScreenPosition(const COORD position) const
|
||||
{
|
||||
// Use shift left to quickly multiply the X pos by 2 for double width lines.
|
||||
const auto scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
|
||||
const SHORT scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
|
||||
return { position.X << scale, position.Y };
|
||||
}
|
||||
|
||||
|
@ -2477,6 +2477,14 @@ const size_t TextBuffer::AddPatternRecognizer(const std::wstring_view regexStrin
|
|||
return _currentPatternId;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Clears the patterns we know of and resets the pattern ID counter
|
||||
void TextBuffer::ClearPatternRecognizers() noexcept
|
||||
{
|
||||
_idsAndPatterns.clear();
|
||||
_currentPatternId = 0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Copies the patterns the other buffer knows about into this one
|
||||
// Arguments:
|
||||
|
|
|
@ -196,6 +196,7 @@ public:
|
|||
std::optional<std::reference_wrapper<PositionInformation>> positionInfo);
|
||||
|
||||
const size_t AddPatternRecognizer(const std::wstring_view regexString);
|
||||
void ClearPatternRecognizers() noexcept;
|
||||
void CopyPatterns(const TextBuffer& OtherBuffer);
|
||||
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const size_t firstRow, const size_t lastRow) const;
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ Author(s):
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "AttrRowIterator.hpp"
|
||||
#include "CharRow.hpp"
|
||||
#include "AttrRow.hpp"
|
||||
#include "OutputCellView.hpp"
|
||||
#include "../../types/inc/viewport.hpp"
|
||||
|
||||
|
@ -55,7 +55,7 @@ protected:
|
|||
OutputCellView _view;
|
||||
|
||||
const ROW* _pRow;
|
||||
AttrRowIterator _attrIter;
|
||||
ATTR_ROW::const_iterator _attrIter;
|
||||
const TextBuffer& _buffer;
|
||||
const Microsoft::Console::Types::Viewport _bounds;
|
||||
bool _exceeded;
|
||||
|
|
|
@ -1,731 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "WexTestClass.h"
|
||||
#include "../../../inc/consoletaeftemplates.hpp"
|
||||
|
||||
#include "../textBuffer.hpp"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
namespace WEX
|
||||
{
|
||||
namespace TestExecution
|
||||
{
|
||||
template<>
|
||||
class VerifyOutputTraits<TextAttributeRun>
|
||||
{
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const TextAttributeRun& tar)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(
|
||||
L"Length:%d, attr:%s",
|
||||
tar.GetLength(),
|
||||
VerifyOutputTraits<TextAttribute>::ToString(tar.GetAttributes()).GetBuffer());
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class VerifyCompareTraits<TextAttributeRun, TextAttributeRun>
|
||||
{
|
||||
public:
|
||||
static bool AreEqual(const TextAttributeRun& expected, const TextAttributeRun& actual)
|
||||
{
|
||||
return expected.GetAttributes() == actual.GetAttributes() &&
|
||||
expected.GetLength() == actual.GetLength();
|
||||
}
|
||||
|
||||
static bool AreSame(const TextAttributeRun& expected, const TextAttributeRun& actual)
|
||||
{
|
||||
return &expected == &actual;
|
||||
}
|
||||
|
||||
static bool IsLessThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
|
||||
|
||||
static bool IsGreaterThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
|
||||
|
||||
static bool IsNull(const TextAttributeRun& object)
|
||||
{
|
||||
return object.GetAttributes().IsLegacy() && object.GetAttributes().GetLegacyAttributes() == 0 &&
|
||||
object.GetLength() == 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class AttrRowTests
|
||||
{
|
||||
ATTR_ROW* pSingle;
|
||||
ATTR_ROW* pChain;
|
||||
|
||||
short _sDefaultLength = 80;
|
||||
short _sDefaultChainLength = 6;
|
||||
|
||||
short sChainSegLength;
|
||||
short sChainLeftover;
|
||||
short sChainSegmentsNeeded;
|
||||
|
||||
WORD __wDefaultAttr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
|
||||
WORD __wDefaultChainAttr = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
|
||||
TextAttribute _DefaultAttr = TextAttribute(__wDefaultAttr);
|
||||
TextAttribute _DefaultChainAttr = TextAttribute(__wDefaultChainAttr);
|
||||
|
||||
TEST_CLASS(AttrRowTests);
|
||||
|
||||
TEST_METHOD_SETUP(MethodSetup)
|
||||
{
|
||||
pSingle = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
|
||||
|
||||
// Segment length is the expected length divided by the row length
|
||||
// E.g. row of 80, 4 segments, 20 segment length each
|
||||
sChainSegLength = _sDefaultLength / _sDefaultChainLength;
|
||||
|
||||
// Leftover is spaces that don't fit evenly
|
||||
// E.g. row of 81, 4 segments, 1 leftover length segment
|
||||
sChainLeftover = _sDefaultLength % _sDefaultChainLength;
|
||||
|
||||
// Start with the number of segments we expect
|
||||
sChainSegmentsNeeded = _sDefaultChainLength;
|
||||
|
||||
// If we had a remainder, add one more segment
|
||||
if (sChainLeftover)
|
||||
{
|
||||
sChainSegmentsNeeded++;
|
||||
}
|
||||
|
||||
// Create the chain
|
||||
pChain = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
|
||||
pChain->_list.resize(sChainSegmentsNeeded);
|
||||
|
||||
// Attach all chain segments that are even multiples of the row length
|
||||
for (short iChain = 0; iChain < _sDefaultChainLength; iChain++)
|
||||
{
|
||||
TextAttributeRun* pRun = &pChain->_list[iChain];
|
||||
|
||||
pRun->SetAttributes(TextAttribute{ gsl::narrow_cast<WORD>(iChain) }); // Just use the chain position as the value
|
||||
pRun->SetLength(sChainSegLength);
|
||||
}
|
||||
|
||||
if (sChainLeftover > 0)
|
||||
{
|
||||
// If we had a leftover, then this chain is one longer than we expected (the default length)
|
||||
// So use it as the index (because indices start at 0)
|
||||
TextAttributeRun* pRun = &pChain->_list[_sDefaultChainLength];
|
||||
|
||||
pRun->SetAttributes(_DefaultChainAttr);
|
||||
pRun->SetLength(sChainLeftover);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_METHOD_CLEANUP(MethodCleanup)
|
||||
{
|
||||
delete pSingle;
|
||||
|
||||
delete pChain;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_METHOD(TestInitialize)
|
||||
{
|
||||
// Properties needed for test
|
||||
const WORD wAttr = FOREGROUND_RED | BACKGROUND_BLUE;
|
||||
TextAttribute attr = TextAttribute(wAttr);
|
||||
// Cases to test
|
||||
ATTR_ROW* pTestItems[]{ pSingle, pChain };
|
||||
|
||||
// Loop cases
|
||||
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
|
||||
{
|
||||
ATTR_ROW* pUnderTest = pTestItems[iIndex];
|
||||
|
||||
pUnderTest->Reset(attr);
|
||||
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), attr);
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Packs an array of words representing attributes into the more compact storage form used by the row.
|
||||
// Arguments:
|
||||
// - rgAttrs - Array of words representing the attribute associated with each character position in the row.
|
||||
// - cRowLength - Length of preceding array.
|
||||
// - outAttrRun - reference to unique_ptr that will contain packed attr run on success.
|
||||
// Return Value:
|
||||
// - Success if success. Buffer too small if row length is incorrect.
|
||||
HRESULT PackAttrs(_In_reads_(cRowLength) const TextAttribute* const rgAttrs,
|
||||
const size_t cRowLength,
|
||||
_Inout_ std::unique_ptr<TextAttributeRun[]>& outAttrRun,
|
||||
_Out_ size_t* const cOutAttrRun)
|
||||
{
|
||||
RETURN_HR_IF(E_NOT_SUFFICIENT_BUFFER, cRowLength == 0);
|
||||
|
||||
// first count up the deltas in the array
|
||||
size_t cDeltas = 1;
|
||||
|
||||
const TextAttribute* pPrevAttr = &rgAttrs[0];
|
||||
|
||||
for (size_t i = 1; i < cRowLength; i++)
|
||||
{
|
||||
const TextAttribute* pCurAttr = &rgAttrs[i];
|
||||
|
||||
if (*pCurAttr != *pPrevAttr)
|
||||
{
|
||||
cDeltas++;
|
||||
}
|
||||
|
||||
pPrevAttr = pCurAttr;
|
||||
}
|
||||
|
||||
// This whole situation was too complicated with a one off holder for one row run
|
||||
// new method:
|
||||
// delete the old buffer
|
||||
// make a new buffer, one run + one run for each change
|
||||
// set the values for each run one run index at a time
|
||||
|
||||
std::unique_ptr<TextAttributeRun[]> attrRun = std::make_unique<TextAttributeRun[]>(cDeltas);
|
||||
RETURN_HR_IF_NULL(E_OUTOFMEMORY, attrRun);
|
||||
|
||||
TextAttributeRun* pCurrentRun = attrRun.get();
|
||||
pCurrentRun->SetAttributes(rgAttrs[0]);
|
||||
pCurrentRun->SetLength(1);
|
||||
for (size_t i = 1; i < cRowLength; i++)
|
||||
{
|
||||
if (pCurrentRun->GetAttributes() == rgAttrs[i])
|
||||
{
|
||||
pCurrentRun->SetLength(pCurrentRun->GetLength() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCurrentRun++;
|
||||
pCurrentRun->SetAttributes(rgAttrs[i]);
|
||||
pCurrentRun->SetLength(1);
|
||||
}
|
||||
}
|
||||
attrRun.swap(outAttrRun);
|
||||
*cOutAttrRun = cDeltas;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
NoThrowString LogRunElement(_In_ TextAttributeRun& run)
|
||||
{
|
||||
return NoThrowString().Format(L"%wc%d", run.GetAttributes().GetLegacyAttributes(), run.GetLength());
|
||||
}
|
||||
|
||||
void LogChain(_In_ PCWSTR pwszPrefix,
|
||||
boost::container::small_vector_base<TextAttributeRun>& chain)
|
||||
{
|
||||
NoThrowString str(pwszPrefix);
|
||||
|
||||
if (chain.size() > 0)
|
||||
{
|
||||
str.Append(LogRunElement(chain[0]));
|
||||
|
||||
for (size_t i = 1; i < chain.size(); i++)
|
||||
{
|
||||
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(str);
|
||||
}
|
||||
|
||||
void LogChain(_In_ PCWSTR pwszPrefix,
|
||||
std::vector<TextAttributeRun>& chain)
|
||||
{
|
||||
NoThrowString str(pwszPrefix);
|
||||
|
||||
if (chain.size() > 0)
|
||||
{
|
||||
str.Append(LogRunElement(chain[0]));
|
||||
|
||||
for (size_t i = 1; i < chain.size(); i++)
|
||||
{
|
||||
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(str);
|
||||
}
|
||||
|
||||
void DoTestInsertAttrRuns(UINT& uiStartPos, WORD& ch1, UINT& uiChar1Length, WORD& ch2, UINT& uiChar2Length)
|
||||
{
|
||||
Log::Comment(String().Format(L"StartPos: %d, Char1: %wc, Char1Length: %d, Char2: %wc, Char2Length: %d",
|
||||
uiStartPos,
|
||||
ch1,
|
||||
uiChar1Length,
|
||||
ch2,
|
||||
uiChar2Length));
|
||||
|
||||
bool const fUseStr2 = (ch2 != L'0');
|
||||
|
||||
// Set up our "original row" that we are going to try to insert into.
|
||||
// This will represent a 10 column run of R3->B5->G2 that we will use for all tests.
|
||||
ATTR_ROW originalRow{ static_cast<UINT>(_sDefaultLength), _DefaultAttr };
|
||||
originalRow._list.resize(3);
|
||||
originalRow._cchRowWidth = 10;
|
||||
originalRow._list[0].SetAttributes(TextAttribute{ 'R' });
|
||||
originalRow._list[0].SetLength(3);
|
||||
originalRow._list[1].SetAttributes(TextAttribute{ 'B' });
|
||||
originalRow._list[1].SetLength(5);
|
||||
originalRow._list[2].SetAttributes(TextAttribute{ 'G' });
|
||||
originalRow._list[2].SetLength(2);
|
||||
LogChain(L"Original: ", originalRow._list);
|
||||
|
||||
// Set up our "insertion run"
|
||||
size_t cInsertRow = 1;
|
||||
if (fUseStr2)
|
||||
{
|
||||
cInsertRow++;
|
||||
}
|
||||
|
||||
std::vector<TextAttributeRun> insertRow;
|
||||
insertRow.resize(cInsertRow);
|
||||
insertRow[0].SetAttributes(TextAttribute{ ch1 });
|
||||
insertRow[0].SetLength(uiChar1Length);
|
||||
if (fUseStr2)
|
||||
{
|
||||
insertRow[1].SetAttributes(TextAttribute{ ch2 });
|
||||
insertRow[1].SetLength(uiChar2Length);
|
||||
}
|
||||
|
||||
LogChain(L"Insert: ", insertRow);
|
||||
Log::Comment(NoThrowString().Format(L"At Index: %d", uiStartPos));
|
||||
|
||||
UINT uiTotalLength = uiChar1Length;
|
||||
if (fUseStr2)
|
||||
{
|
||||
uiTotalLength += uiChar2Length;
|
||||
}
|
||||
|
||||
VERIFY_IS_TRUE((uiStartPos + uiTotalLength) >= 1); // assert we won't underflow.
|
||||
UINT const uiEndPos = uiStartPos + uiTotalLength - 1;
|
||||
|
||||
// Calculate our expected final/result run by unpacking original, laying our insertion on it at the index
|
||||
// then using our pack function to repack it.
|
||||
// This method is easy to understand and very reliable, but its performance is bad.
|
||||
// The InsertAttrRuns method we test against below is hard to understand but very high performance in production.
|
||||
|
||||
// - 1. Unpack
|
||||
std::vector<TextAttribute> unpackedOriginal = { originalRow.begin(), originalRow.end() };
|
||||
|
||||
// - 2. Overlay insertion
|
||||
UINT uiInsertedCount = 0;
|
||||
UINT uiInsertIndex = 0;
|
||||
|
||||
// --- Walk through the unpacked run from start to end....
|
||||
for (UINT uiUnpackedIndex = uiStartPos; uiUnpackedIndex <= uiEndPos; uiUnpackedIndex++)
|
||||
{
|
||||
// Pull the item from the insert run to analyze.
|
||||
TextAttributeRun run = insertRow[uiInsertIndex];
|
||||
|
||||
// Copy the attribute from the run into the unpacked array
|
||||
unpackedOriginal[uiUnpackedIndex] = run.GetAttributes();
|
||||
|
||||
// Increment how many times we've copied this particular portion of the run
|
||||
uiInsertedCount++;
|
||||
|
||||
// If we've now inserted enough of them to match the length, advance the insert index and reset the counter.
|
||||
if (uiInsertedCount >= run.GetLength())
|
||||
{
|
||||
uiInsertIndex++;
|
||||
uiInsertedCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// - 3. Pack.
|
||||
std::unique_ptr<TextAttributeRun[]> packedRun;
|
||||
size_t cPackedRun = 0;
|
||||
VERIFY_SUCCEEDED(PackAttrs(unpackedOriginal.data(), originalRow._cchRowWidth, packedRun, &cPackedRun));
|
||||
|
||||
// Now send parameters into InsertAttrRuns and get its opinion on the subject.
|
||||
VERIFY_SUCCEEDED(originalRow.InsertAttrRuns({ insertRow.data(), insertRow.size() }, uiStartPos, uiEndPos, (UINT)originalRow._cchRowWidth));
|
||||
|
||||
// Compare and ensure that the expected and actual match.
|
||||
VERIFY_ARE_EQUAL(cPackedRun, originalRow._list.size(), L"Ensure that number of array elements required for RLE are the same.");
|
||||
|
||||
std::vector<TextAttributeRun> packedRunExpected;
|
||||
std::copy_n(packedRun.get(), cPackedRun, std::back_inserter(packedRunExpected));
|
||||
|
||||
LogChain(L"Expected: ", packedRunExpected);
|
||||
LogChain(L"Actual: ", originalRow._list);
|
||||
|
||||
for (size_t testIndex = 0; testIndex < cPackedRun; testIndex++)
|
||||
{
|
||||
VERIFY_ARE_EQUAL(packedRun[testIndex], originalRow._list[testIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestInsertAttrRunsSingle)
|
||||
{
|
||||
UINT const uiTestRunLength = 10;
|
||||
|
||||
UINT uiStartPos = 0;
|
||||
WORD ch1 = L'0';
|
||||
UINT uiChar1Length = 0;
|
||||
WORD ch2 = L'0';
|
||||
UINT uiChar2Length = 0;
|
||||
|
||||
Log::Comment(L"Test inserting a single item of a variable length into the run.");
|
||||
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
|
||||
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
|
||||
{
|
||||
ch1 = rgch1Options[iCh1Option];
|
||||
for (UINT iCh1Length = 1; iCh1Length <= uiTestRunLength; iCh1Length++)
|
||||
{
|
||||
uiChar1Length = iCh1Length;
|
||||
|
||||
// We can't try to insert a run that's longer than would fit.
|
||||
// If the run is of length 10 and we're trying to insert a length of 10,
|
||||
// we can only insert at position 0.
|
||||
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
|
||||
// And so on...
|
||||
UINT const uiMaxPos = uiTestRunLength - uiChar1Length;
|
||||
|
||||
for (UINT iStartPos = 0; iStartPos < uiMaxPos; iStartPos++)
|
||||
{
|
||||
uiStartPos = iStartPos;
|
||||
|
||||
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestInsertAttrRunsMultiple)
|
||||
{
|
||||
UINT const uiTestRunLength = 10;
|
||||
|
||||
UINT uiStartPos = 0;
|
||||
WORD ch1 = L'0';
|
||||
UINT uiChar1Length = 0;
|
||||
WORD ch2 = L'0';
|
||||
UINT uiChar2Length = 0;
|
||||
|
||||
Log::Comment(L"Test inserting a multiple item run with each piece having variable length into the existing run.");
|
||||
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
|
||||
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
|
||||
{
|
||||
ch1 = rgch1Options[iCh1Option];
|
||||
|
||||
UINT const uiMaxCh1Length = uiTestRunLength - 1; // leave at least 1 space for the second piece of the insert run.
|
||||
for (UINT iCh1Length = 1; iCh1Length <= uiMaxCh1Length; iCh1Length++)
|
||||
{
|
||||
uiChar1Length = iCh1Length;
|
||||
|
||||
WORD rgch2Options[] = { L'Y' };
|
||||
for (size_t iCh2Option = 0; iCh2Option < ARRAYSIZE(rgch2Options); iCh2Option++)
|
||||
{
|
||||
ch2 = rgch2Options[iCh2Option];
|
||||
|
||||
// When choosing the length of the second item, it can't be bigger than the remaining space in the run
|
||||
// when accounting for the length of the first piece chosen.
|
||||
// For example if the total run length is 10 and the first piece chosen was 8 long,
|
||||
// the second piece can only be 1 or 2 long.
|
||||
UINT const uiMaxCh2Length = uiTestRunLength - uiMaxCh1Length;
|
||||
for (UINT iCh2Length = 1; iCh2Length <= uiMaxCh2Length; iCh2Length++)
|
||||
{
|
||||
uiChar2Length = iCh2Length;
|
||||
|
||||
// We can't try to insert a run that's longer than would fit.
|
||||
// If the run is of length 10 and we're trying to insert a total length of 10,
|
||||
// we can only insert at position 0.
|
||||
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
|
||||
// And so on...
|
||||
UINT const uiMaxPos = uiTestRunLength - (uiChar1Length + uiChar2Length);
|
||||
|
||||
for (UINT iStartPos = 0; iStartPos <= uiMaxPos; iStartPos++)
|
||||
{
|
||||
uiStartPos = iStartPos;
|
||||
|
||||
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestUnpackAttrs)
|
||||
{
|
||||
Log::Comment(L"Checking unpack of a single color for the entire length");
|
||||
{
|
||||
const std::vector<TextAttribute> attrs{ pSingle->begin(), pSingle->end() };
|
||||
|
||||
for (auto& attr : attrs)
|
||||
{
|
||||
VERIFY_ARE_EQUAL(attr, _DefaultAttr);
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(L"Checking unpack of the multiple color chain");
|
||||
|
||||
const std::vector<TextAttribute> attrs{ pChain->begin(), pChain->end() };
|
||||
|
||||
short cChainRun = 0; // how long we've been looking at the current piece of the chain
|
||||
short iChainSegIndex = 0; // which piece of the chain we should be on right now
|
||||
|
||||
for (auto& attr : attrs)
|
||||
{
|
||||
// by default the chain was assembled above to have the chain segment index be the attribute
|
||||
TextAttribute MatchingAttr = TextAttribute(iChainSegIndex);
|
||||
|
||||
// However, if the index is greater than the expected chain length, a remainder piece was made with a default attribute
|
||||
if (iChainSegIndex >= _sDefaultChainLength)
|
||||
{
|
||||
MatchingAttr = _DefaultChainAttr;
|
||||
}
|
||||
|
||||
VERIFY_ARE_EQUAL(attr, MatchingAttr);
|
||||
|
||||
// Add to the chain run
|
||||
cChainRun++;
|
||||
|
||||
// If the chain run is greater than the length the segments were specified to be
|
||||
if (cChainRun >= sChainSegLength)
|
||||
{
|
||||
// reset to 0
|
||||
cChainRun = 0;
|
||||
|
||||
// move to the next chain segment down the line
|
||||
iChainSegIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestReverseIteratorWalkFromMiddle)
|
||||
{
|
||||
// GH #3409, walking backwards through color range runs out of bounds
|
||||
// We're going to create an attribute row with assorted colors and varying lengths
|
||||
// just like the row of text on the Ubuntu prompt line that triggered this bug being found.
|
||||
// Then we're going to walk backwards through the iterator like a selection-expand-to-left
|
||||
// operation and ensure we don't run off the bounds.
|
||||
|
||||
// walk the chain, from index, stepSize at a time
|
||||
// ensure we don't crash
|
||||
auto testWalk = [](ATTR_ROW* chain, size_t index, int stepSize) {
|
||||
// move to starting index
|
||||
auto iter = chain->cbegin();
|
||||
iter += index;
|
||||
|
||||
// Now walk backwards in a loop until 0.
|
||||
while (iter)
|
||||
{
|
||||
iter -= stepSize;
|
||||
}
|
||||
|
||||
Log::Comment(L"We made it through without crashing!");
|
||||
};
|
||||
|
||||
// take one step of size stepSize on the chain
|
||||
// index is where we start from
|
||||
// expectedAttribute is what we expect to read here
|
||||
auto verifyStep = [](ATTR_ROW* chain, size_t index, int stepSize, TextAttribute expectedAttribute) {
|
||||
// move to starting index
|
||||
auto iter = chain->cbegin();
|
||||
iter += index;
|
||||
|
||||
// Now step backwards
|
||||
iter -= stepSize;
|
||||
|
||||
VERIFY_ARE_EQUAL(expectedAttribute, *iter);
|
||||
};
|
||||
|
||||
Log::Comment(L"Reverse iterate through ubuntu prompt");
|
||||
{
|
||||
// Create attr row representing a buffer that's 121 wide.
|
||||
auto chain = std::make_unique<ATTR_ROW>(121, _DefaultAttr);
|
||||
|
||||
// The repro case had 4 chain segments.
|
||||
chain->_list.resize(4);
|
||||
|
||||
// The color 10 went for the first 18.
|
||||
chain->_list[0].SetAttributes(TextAttribute(0xA));
|
||||
chain->_list[0].SetLength(18);
|
||||
|
||||
// Default color for the next 1
|
||||
chain->_list[1].SetAttributes(TextAttribute());
|
||||
chain->_list[1].SetLength(1);
|
||||
|
||||
// Color 12 for the next 29
|
||||
chain->_list[2].SetAttributes(TextAttribute(0xC));
|
||||
chain->_list[2].SetLength(29);
|
||||
|
||||
// Then default color to end the run
|
||||
chain->_list[3].SetAttributes(TextAttribute());
|
||||
chain->_list[3].SetLength(73);
|
||||
|
||||
// The sum of the lengths should be 121.
|
||||
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength + chain->_list[3]._cchLength);
|
||||
|
||||
auto index = chain->_list[0].GetLength();
|
||||
auto stepSize = 1;
|
||||
testWalk(chain.get(), index, stepSize);
|
||||
}
|
||||
|
||||
Log::Comment(L"Reverse iterate across a text run in the chain");
|
||||
{
|
||||
// Create attr row representing a buffer that's 3 wide.
|
||||
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
|
||||
|
||||
// The repro case had 3 chain segments.
|
||||
chain->_list.resize(3);
|
||||
|
||||
// The color 10 went for the first 1.
|
||||
chain->_list[0].SetAttributes(TextAttribute(0xA));
|
||||
chain->_list[0].SetLength(1);
|
||||
|
||||
// The color 11 for the next 1
|
||||
chain->_list[1].SetAttributes(TextAttribute(0xB));
|
||||
chain->_list[1].SetLength(1);
|
||||
|
||||
// Color 12 for the next 1
|
||||
chain->_list[2].SetAttributes(TextAttribute(0xC));
|
||||
chain->_list[2].SetLength(1);
|
||||
|
||||
// The sum of the lengths should be 3.
|
||||
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
|
||||
|
||||
// on 'ABC', step from B to A
|
||||
auto index = 1;
|
||||
auto stepSize = 1;
|
||||
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
|
||||
}
|
||||
|
||||
Log::Comment(L"Reverse iterate across two text runs in the chain");
|
||||
{
|
||||
// Create attr row representing a buffer that's 3 wide.
|
||||
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
|
||||
|
||||
// The repro case had 3 chain segments.
|
||||
chain->_list.resize(3);
|
||||
|
||||
// The color 10 went for the first 1.
|
||||
chain->_list[0].SetAttributes(TextAttribute(0xA));
|
||||
chain->_list[0].SetLength(1);
|
||||
|
||||
// The color 11 for the next 1
|
||||
chain->_list[1].SetAttributes(TextAttribute(0xB));
|
||||
chain->_list[1].SetLength(1);
|
||||
|
||||
// Color 12 for the next 1
|
||||
chain->_list[2].SetAttributes(TextAttribute(0xC));
|
||||
chain->_list[2].SetLength(1);
|
||||
|
||||
// The sum of the lengths should be 3.
|
||||
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
|
||||
|
||||
// on 'ABC', step from C to A
|
||||
auto index = 2;
|
||||
auto stepSize = 2;
|
||||
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestSetAttrToEnd)
|
||||
{
|
||||
const WORD wTestAttr = FOREGROUND_BLUE | BACKGROUND_GREEN;
|
||||
TextAttribute TestAttr = TextAttribute(wTestAttr);
|
||||
|
||||
Log::Comment(L"FIRST: Set index to > 0 to test making/modifying chains");
|
||||
const short iTestIndex = 50;
|
||||
VERIFY_IS_TRUE(iTestIndex >= 0 && iTestIndex < _sDefaultLength);
|
||||
|
||||
Log::Comment(L"SetAttrToEnd for single color applied to whole string.");
|
||||
pSingle->SetAttrToEnd(iTestIndex, TestAttr);
|
||||
|
||||
// Was 1 (single), should now have 2 segments
|
||||
VERIFY_ARE_EQUAL(pSingle->_list.size(), 2u);
|
||||
|
||||
VERIFY_ARE_EQUAL(pSingle->_list[0].GetAttributes(), _DefaultAttr);
|
||||
VERIFY_ARE_EQUAL(pSingle->_list[0].GetLength(), (unsigned int)(_sDefaultLength - (_sDefaultLength - iTestIndex)));
|
||||
|
||||
VERIFY_ARE_EQUAL(pSingle->_list[1].GetAttributes(), TestAttr);
|
||||
VERIFY_ARE_EQUAL(pSingle->_list[1].GetLength(), (unsigned int)(_sDefaultLength - iTestIndex));
|
||||
|
||||
Log::Comment(L"SetAttrToEnd for existing chain of multiple colors.");
|
||||
pChain->SetAttrToEnd(iTestIndex, TestAttr);
|
||||
|
||||
// From 7 segments down to 5.
|
||||
VERIFY_ARE_EQUAL(pChain->_list.size(), 5u);
|
||||
|
||||
// Verify chain colors and lengths
|
||||
VERIFY_ARE_EQUAL(TextAttribute(0), pChain->_list[0].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[0].GetLength(), (unsigned int)13);
|
||||
|
||||
VERIFY_ARE_EQUAL(TextAttribute(1), pChain->_list[1].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[1].GetLength(), (unsigned int)13);
|
||||
|
||||
VERIFY_ARE_EQUAL(TextAttribute(2), pChain->_list[2].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[2].GetLength(), (unsigned int)13);
|
||||
|
||||
VERIFY_ARE_EQUAL(TextAttribute(3), pChain->_list[3].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[3].GetLength(), (unsigned int)11);
|
||||
|
||||
VERIFY_ARE_EQUAL(TestAttr, pChain->_list[4].GetAttributes());
|
||||
VERIFY_ARE_EQUAL(pChain->_list[4].GetLength(), (unsigned int)30);
|
||||
|
||||
Log::Comment(L"SECOND: Set index to 0 to test replacing anything with a single");
|
||||
|
||||
ATTR_ROW* pTestItems[]{ pSingle, pChain };
|
||||
|
||||
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
|
||||
{
|
||||
ATTR_ROW* pUnderTest = pTestItems[iIndex];
|
||||
|
||||
pUnderTest->SetAttrToEnd(0, TestAttr);
|
||||
|
||||
// should be down to 1 attribute set from beginning to end of string
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
|
||||
|
||||
// singular pair should contain the color
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), TestAttr);
|
||||
|
||||
// and its length should be the length of the whole string
|
||||
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestTotalLength)
|
||||
{
|
||||
ATTR_ROW* pTestItems[]{ pSingle, pChain };
|
||||
|
||||
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
|
||||
{
|
||||
ATTR_ROW* pUnderTest = pTestItems[iIndex];
|
||||
|
||||
const size_t Result = pUnderTest->_cchRowWidth;
|
||||
|
||||
VERIFY_ARE_EQUAL((short)Result, _sDefaultLength);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TestResize)
|
||||
{
|
||||
pSingle->Resize(240);
|
||||
pChain->Resize(240);
|
||||
|
||||
pSingle->Resize(255);
|
||||
pChain->Resize(255);
|
||||
|
||||
pSingle->Resize(255);
|
||||
pChain->Resize(255);
|
||||
|
||||
pSingle->Resize(60);
|
||||
pChain->Resize(60);
|
||||
|
||||
pSingle->Resize(60);
|
||||
pChain->Resize(60);
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(pSingle->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
|
||||
VERIFY_THROWS_SPECIFIC(pChain->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
|
||||
}
|
||||
};
|
|
@ -6,11 +6,10 @@
|
|||
<RootNamespace>TextBufferUnitTests</RootNamespace>
|
||||
<ProjectName>TextBuffer.Unit.Tests</ProjectName>
|
||||
<TargetName>TextBuffer.Unit.Tests</TargetName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AttrRowTests.cpp" />
|
||||
<ClCompile Include="ReflowTests.cpp" />
|
||||
<ClCompile Include="TextColorTests.cpp" />
|
||||
<ClCompile Include="TextAttributeTests.cpp" />
|
||||
|
|
|
@ -14,7 +14,6 @@ DLLDEF =
|
|||
|
||||
SOURCES = \
|
||||
$(SOURCES) \
|
||||
AttrRowTests.cpp \
|
||||
ReflowTests.cpp \
|
||||
TextColorTests.cpp \
|
||||
TextAttributeTests.cpp \
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\wap-common.build.pre.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
|
||||
<!--
|
||||
These two properties are very important!
|
||||
Without them, msbuild will stomp MinVersion and MaxVersionTested in the
|
||||
|
@ -15,10 +13,6 @@
|
|||
<AppxOSMaxVersionTestedReplaceManifestVersion>false</AppxOSMaxVersionTestedReplaceManifestVersion>
|
||||
<OCExecutionAliasName Condition="'$(WindowsTerminalBranding)'==''">wtd</OCExecutionAliasName>
|
||||
<OCExecutionAliasName Condition="'$(OCExecutionAliasName)'==''">wt</OCExecutionAliasName>
|
||||
<!-- VS 16.8 causes WAP projects to accidentally package System.Core.dll (from the CLR).
|
||||
This has been fixed in VS 16.9 using the property below. It's safe for us to include
|
||||
it here, even after 16.9 comes out. -->
|
||||
<AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>CA5CAD1A-224A-4171-B13A-F16E576FDD12</ProjectGuid>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
@ -99,7 +99,7 @@
|
|||
<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:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
|
||||
</com:ComInterface>
|
||||
</com:Extension>
|
||||
<com:Extension Category="windows.comServer">
|
||||
|
@ -122,7 +122,7 @@
|
|||
</desktop5:ItemType>
|
||||
<desktop5:ItemType Type="Directory\Background">
|
||||
<desktop5:Verb Id="OpenTerminalDev" Clsid="52065414-e077-47ec-a3ac-1cc5455e1b54" />
|
||||
</desktop5:ItemType>
|
||||
</desktop5:ItemType>
|
||||
</desktop4:FileExplorerContextMenus>
|
||||
</desktop4:Extension>
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
@ -100,7 +100,7 @@
|
|||
<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:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="1833E661-CC81-4DD0-87C6-C2F74BD39EFA"/>
|
||||
</com:ComInterface>
|
||||
</com:Extension>
|
||||
<com:Extension Category="windows.comServer">
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
@ -100,7 +100,7 @@
|
|||
<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:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
|
||||
</com:ComInterface>
|
||||
</com:Extension> -->
|
||||
<com:Extension Category="windows.comServer">
|
||||
|
|
|
@ -106,9 +106,9 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
auto command = commands.Lookup(L"action0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
}
|
||||
{
|
||||
|
@ -117,9 +117,9 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
auto command = commands.Lookup(L"action0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::PasteText, command.Action().Action());
|
||||
VERIFY_IS_NULL(command.Action().Args());
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::PasteText, command.ActionAndArgs().Action());
|
||||
VERIFY_IS_NULL(command.ActionAndArgs().Args());
|
||||
}
|
||||
{
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands2Json);
|
||||
|
@ -127,9 +127,9 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
auto command = commands.Lookup(L"action0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
}
|
||||
{
|
||||
|
@ -165,9 +165,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"command1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
|
@ -176,9 +176,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"command2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
|
@ -187,9 +187,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"command4");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
|
@ -198,9 +198,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"command5");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
|
@ -209,9 +209,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"command6");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
|
@ -239,9 +239,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"command1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
|
@ -268,9 +268,9 @@ namespace SettingsModelLocalTests
|
|||
// this test will break.
|
||||
auto command = commands.Lookup(L"Duplicate tab");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
}
|
||||
}
|
||||
|
@ -309,9 +309,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"Split pane");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
|
@ -319,9 +319,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"Split pane, split: vertical");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
|
@ -329,9 +329,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"Split pane, split: horizontal");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
|
@ -355,9 +355,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"Split pane");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
|
@ -410,9 +410,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"action0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
|
@ -423,9 +423,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"action1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
|
@ -436,9 +436,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"action2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
|
@ -449,9 +449,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"action3");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
|
@ -462,9 +462,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"action4");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
|
@ -477,9 +477,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"action5");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
|
@ -492,9 +492,9 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
auto command = commands.Lookup(L"action6");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
|
|
|
@ -88,6 +88,8 @@ namespace SettingsModelLocalTests
|
|||
|
||||
TEST_METHOD(TestValidDefaults);
|
||||
|
||||
TEST_METHOD(TestInheritedCommand);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
|
@ -1918,7 +1920,12 @@ namespace SettingsModelLocalTests
|
|||
const auto settingsObject = VerifyParseSucceeded(badSettings);
|
||||
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_globals->_keymap->_keyShortcuts.size());
|
||||
// KeyMap: ctrl+a/b are mapped to "invalid"
|
||||
// ActionMap: "splitPane" and "invalid" are the only deserialized actions
|
||||
// NameMap: "splitPane" has no key binding, but it is still added to the name map
|
||||
VERIFY_ARE_EQUAL(2u, settings->_globals->_actionMap->_KeyMap.size());
|
||||
VERIFY_ARE_EQUAL(2u, settings->_globals->_actionMap->_ActionMap.size());
|
||||
VERIFY_ARE_EQUAL(1u, settings->_globals->_actionMap->NameMap().Size());
|
||||
|
||||
VERIFY_ARE_EQUAL(4u, settings->_globals->_keybindingsWarnings.size());
|
||||
VERIFY_ARE_EQUAL(SettingsLoadWarnings::TooManyKeysForChord, settings->_globals->_keybindingsWarnings.at(0));
|
||||
|
@ -1962,7 +1969,10 @@ namespace SettingsModelLocalTests
|
|||
|
||||
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_globals->_keymap->_keyShortcuts.size());
|
||||
VERIFY_ARE_EQUAL(3u, settings->_globals->_actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('a') }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('b') }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
|
||||
|
||||
for (const auto& warning : settings->_globals->_keybindingsWarnings)
|
||||
{
|
||||
|
@ -2103,18 +2113,18 @@ namespace SettingsModelLocalTests
|
|||
const auto profile2Guid = settings->_allProfiles.GetAt(2).Guid();
|
||||
VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
|
||||
|
||||
auto keymap = winrt::get_self<implementation::KeyMapping>(settings->_globals->KeyMap());
|
||||
VERIFY_ARE_EQUAL(5u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::get_self<implementation::ActionMap>(settings->_globals->ActionMap());
|
||||
VERIFY_ARE_EQUAL(5u, actionMap->_KeyMap.size());
|
||||
|
||||
// A/D, B, C, E will be in the list of commands, for 4 total.
|
||||
// * A and D share the same name, so they'll only generate a single action.
|
||||
// * F's name is set manually to `null`
|
||||
auto commands = settings->_globals->Commands();
|
||||
VERIFY_ARE_EQUAL(4u, commands.Size());
|
||||
const auto& nameMap{ actionMap->NameMap() };
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -2131,7 +2141,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -2145,7 +2155,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -2159,7 +2169,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -2173,7 +2183,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -2187,11 +2197,21 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
|
||||
Log::Comment(L"Now verify the commands");
|
||||
_logCommandNames(commands);
|
||||
_logCommandNames(nameMap);
|
||||
{
|
||||
auto command = commands.Lookup(L"Split pane, split: vertical");
|
||||
// This was renamed to "ctrl+c" in C. So this does not exist.
|
||||
auto command = nameMap.TryLookup(L"Split pane, split: vertical");
|
||||
VERIFY_IS_NULL(command);
|
||||
}
|
||||
{
|
||||
// This was renamed to "ctrl+c" in C. So this does not exist.
|
||||
auto command = nameMap.TryLookup(L"ctrl+b");
|
||||
VERIFY_IS_NULL(command);
|
||||
}
|
||||
{
|
||||
auto command = nameMap.TryLookup(L"ctrl+c");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -2205,52 +2225,9 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"ctrl+b");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"ctrl+c");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"Split pane, split: horizontal");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
// This was renamed to null (aka removed from the name map) in F. So this does not exist.
|
||||
auto command = nameMap.TryLookup(L"Split pane, split: horizontal");
|
||||
VERIFY_IS_NULL(command);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2308,16 +2285,16 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
|
||||
|
||||
auto commands = settings->_globals->Commands();
|
||||
settings->_ValidateSettings();
|
||||
_logCommandNames(commands);
|
||||
const auto& nameMap{ settings->ActionMap().NameMap() };
|
||||
_logCommandNames(nameMap);
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
|
||||
// Because the "parent" command didn't have a name, it couldn't be
|
||||
// placed into the list of commands. It and it's children are just
|
||||
// ignored.
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(0u, nameMap.Size());
|
||||
}
|
||||
|
||||
void DeserializationTests::TestNestedCommandWithBadSubCommands()
|
||||
|
@ -2358,13 +2335,13 @@ namespace SettingsModelLocalTests
|
|||
auto settings = winrt::make_self<implementation::CascadiaSettings>();
|
||||
settings->_ParseJsonString(settingsJson, false);
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
auto commands = settings->_globals->Commands();
|
||||
settings->_ValidateSettings();
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->_warnings.GetAt(1));
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
const auto& nameMap{ settings->ActionMap().NameMap() };
|
||||
VERIFY_ARE_EQUAL(0u, nameMap.Size());
|
||||
}
|
||||
|
||||
void DeserializationTests::TestUnbindNestedCommand()
|
||||
|
@ -2432,22 +2409,23 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
|
||||
|
||||
auto commands = settings->_globals->Commands();
|
||||
settings->_ValidateSettings();
|
||||
_logCommandNames(commands);
|
||||
auto nameMap{ settings->ActionMap().NameMap() };
|
||||
_logCommandNames(nameMap);
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
Log::Comment(L"Layer second bit of json, to unbind the original command.");
|
||||
|
||||
settings->_ParseJsonString(settings1Json, false);
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
settings->_ValidateSettings();
|
||||
commands = settings->_globals->Commands();
|
||||
_logCommandNames(commands);
|
||||
|
||||
nameMap = settings->ActionMap().NameMap();
|
||||
_logCommandNames(nameMap);
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(0u, nameMap.Size());
|
||||
}
|
||||
|
||||
void DeserializationTests::TestRebindNestedCommand()
|
||||
|
@ -2516,16 +2494,17 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
|
||||
|
||||
auto commands = settings->_globals->Commands();
|
||||
const auto& actionMap{ settings->ActionMap() };
|
||||
settings->_ValidateSettings();
|
||||
_logCommandNames(commands);
|
||||
auto nameMap{ actionMap.NameMap() };
|
||||
_logCommandNames(nameMap);
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
{
|
||||
winrt::hstring commandName{ L"parent" };
|
||||
auto commandProj = commands.Lookup(commandName);
|
||||
auto commandProj = nameMap.TryLookup(commandName);
|
||||
VERIFY_IS_NOT_NULL(commandProj);
|
||||
|
||||
winrt::com_ptr<implementation::Command> commandImpl;
|
||||
|
@ -2539,17 +2518,18 @@ namespace SettingsModelLocalTests
|
|||
settings->_ParseJsonString(settings1Json, false);
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
settings->_ValidateSettings();
|
||||
commands = settings->_globals->Commands();
|
||||
_logCommandNames(commands);
|
||||
|
||||
nameMap = settings->ActionMap().NameMap();
|
||||
_logCommandNames(nameMap);
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
{
|
||||
winrt::hstring commandName{ L"parent" };
|
||||
auto commandProj = commands.Lookup(commandName);
|
||||
auto commandProj = nameMap.TryLookup(commandName);
|
||||
|
||||
VERIFY_IS_NOT_NULL(commandProj);
|
||||
auto actionAndArgs = commandProj.Action();
|
||||
auto actionAndArgs = commandProj.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
|
@ -2642,8 +2622,10 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(settings->_globals->_colorSchemes.HasKey(schemeName), copyImpl->_globals->_colorSchemes.HasKey(schemeName));
|
||||
|
||||
// test actions
|
||||
VERIFY_ARE_EQUAL(settings->_globals->_keymap->_keyShortcuts.size(), copyImpl->_globals->_keymap->_keyShortcuts.size());
|
||||
VERIFY_ARE_EQUAL(settings->_globals->_commands.Size(), copyImpl->_globals->_commands.Size());
|
||||
VERIFY_ARE_EQUAL(settings->_globals->_actionMap->_KeyMap.size(), copyImpl->_globals->_actionMap->_KeyMap.size());
|
||||
const auto& nameMapOriginal{ settings->_globals->_actionMap->NameMap() };
|
||||
const auto& nameMapCopy{ copyImpl->_globals->_actionMap->NameMap() };
|
||||
VERIFY_ARE_EQUAL(nameMapOriginal.Size(), nameMapCopy.Size());
|
||||
|
||||
// Test that changing the copy should not change the original
|
||||
VERIFY_ARE_EQUAL(settings->_globals->WordDelimiters(), copyImpl->_globals->WordDelimiters());
|
||||
|
@ -2781,4 +2763,158 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(settings.ActiveProfiles().Size(), settings.AllProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(settings.AllProfiles().Size(), 2u);
|
||||
}
|
||||
|
||||
void DeserializationTests::TestInheritedCommand()
|
||||
{
|
||||
// Test unbinding a command's key chord or name that originated in another layer.
|
||||
|
||||
const std::string settings1Json{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1,
|
||||
"commandline": "cmd.exe"
|
||||
},
|
||||
{
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2,
|
||||
"commandline": "pwsh.exe"
|
||||
},
|
||||
{
|
||||
"name": "profile2",
|
||||
"historySize": 3,
|
||||
"commandline": "wsl.exe"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"name": "foo",
|
||||
"command": "closePane",
|
||||
"keys": "ctrl+shift+w"
|
||||
}
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
})" };
|
||||
|
||||
const std::string settings2Json{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"actions": [
|
||||
{
|
||||
"command": null,
|
||||
"keys": "ctrl+shift+w"
|
||||
},
|
||||
],
|
||||
})" };
|
||||
|
||||
const std::string settings3Json{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"actions": [
|
||||
{
|
||||
"name": "bar",
|
||||
"command": "closePane"
|
||||
},
|
||||
],
|
||||
})" };
|
||||
|
||||
VerifyParseSucceeded(settings1Json);
|
||||
VerifyParseSucceeded(settings2Json);
|
||||
VerifyParseSucceeded(settings3Json);
|
||||
|
||||
auto settings = winrt::make_self<implementation::CascadiaSettings>();
|
||||
settings->_ParseJsonString(settings1Json, false);
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
|
||||
|
||||
settings->_ValidateSettings();
|
||||
auto nameMap{ settings->ActionMap().NameMap() };
|
||||
_logCommandNames(nameMap);
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
const KeyChord expectedKeyChord{ true, false, true, static_cast<int>('W') };
|
||||
{
|
||||
// Verify NameMap returns correct value
|
||||
const auto& cmd{ nameMap.TryLookup(L"foo") };
|
||||
VERIFY_IS_NOT_NULL(cmd);
|
||||
VERIFY_IS_NOT_NULL(cmd.Keys());
|
||||
VERIFY_IS_TRUE(cmd.Keys().Modifiers() == expectedKeyChord.Modifiers() && cmd.Keys().Vkey() == expectedKeyChord.Vkey());
|
||||
}
|
||||
{
|
||||
// Verify ActionMap::GetActionByKeyChord API
|
||||
const auto& cmd{ settings->ActionMap().GetActionByKeyChord(expectedKeyChord) };
|
||||
VERIFY_IS_NOT_NULL(cmd);
|
||||
VERIFY_IS_NOT_NULL(cmd.Keys());
|
||||
VERIFY_IS_TRUE(cmd.Keys().Modifiers() == expectedKeyChord.Modifiers() && cmd.Keys().Vkey() == expectedKeyChord.Vkey());
|
||||
}
|
||||
{
|
||||
// Verify ActionMap::GetKeyBindingForAction API
|
||||
const auto& actualKeyChord{ settings->ActionMap().GetKeyBindingForAction(ShortcutAction::ClosePane) };
|
||||
VERIFY_IS_NOT_NULL(actualKeyChord);
|
||||
VERIFY_IS_TRUE(actualKeyChord.Modifiers() == expectedKeyChord.Modifiers() && actualKeyChord.Vkey() == expectedKeyChord.Vkey());
|
||||
}
|
||||
|
||||
Log::Comment(L"Layer second bit of json, to unbind the key chord of the original command.");
|
||||
|
||||
settings->_ParseJsonString(settings2Json, false);
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
settings->_ValidateSettings();
|
||||
|
||||
nameMap = settings->ActionMap().NameMap();
|
||||
_logCommandNames(nameMap);
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
{
|
||||
// Verify NameMap returns correct value
|
||||
const auto& cmd{ nameMap.TryLookup(L"foo") };
|
||||
VERIFY_IS_NOT_NULL(cmd);
|
||||
VERIFY_IS_NULL(cmd.Keys());
|
||||
}
|
||||
{
|
||||
// Verify ActionMap::GetActionByKeyChord API
|
||||
const auto& cmd{ settings->ActionMap().GetActionByKeyChord(expectedKeyChord) };
|
||||
VERIFY_IS_NULL(cmd);
|
||||
}
|
||||
{
|
||||
// Verify ActionMap::GetKeyBindingForAction API
|
||||
const auto& actualKeyChord{ settings->ActionMap().GetKeyBindingForAction(ShortcutAction::ClosePane) };
|
||||
VERIFY_IS_NULL(actualKeyChord);
|
||||
}
|
||||
|
||||
Log::Comment(L"Layer third bit of json, to unbind name of the original command.");
|
||||
|
||||
settings->_ParseJsonString(settings3Json, false);
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
settings->_ValidateSettings();
|
||||
|
||||
nameMap = settings->ActionMap().NameMap();
|
||||
_logCommandNames(nameMap);
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
{
|
||||
// Verify NameMap returns correct value
|
||||
const auto& cmd{ nameMap.TryLookup(L"bar") };
|
||||
VERIFY_IS_NOT_NULL(cmd);
|
||||
VERIFY_IS_NULL(cmd.Keys());
|
||||
VERIFY_ARE_EQUAL(L"bar", cmd.Name());
|
||||
}
|
||||
{
|
||||
// Verify ActionMap::GetActionByKeyChord API
|
||||
const auto& cmd{ settings->ActionMap().GetActionByKeyChord(expectedKeyChord) };
|
||||
VERIFY_IS_NULL(cmd);
|
||||
}
|
||||
{
|
||||
// Verify ActionMap::GetKeyBindingForAction API
|
||||
const auto& actualKeyChord{ settings->ActionMap().GetKeyBindingForAction(ShortcutAction::ClosePane) };
|
||||
VERIFY_IS_NULL(actualKeyChord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "../TerminalSettingsModel/ColorScheme.h"
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "../TerminalSettingsModel/KeyMapping.h"
|
||||
#include "../TerminalSettingsModel/ActionMap.h"
|
||||
#include "JsonTestClass.h"
|
||||
#include "TestUtils.h"
|
||||
|
||||
|
@ -51,6 +51,8 @@ namespace SettingsModelLocalTests
|
|||
TEST_METHOD(TestToggleCommandPaletteArgs);
|
||||
TEST_METHOD(TestMoveTabArgs);
|
||||
|
||||
TEST_METHOD(TestGetKeyBindingForAction);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
|
@ -71,18 +73,18 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
|
||||
keymap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(2u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
|
||||
keymap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(4u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::LayerKeybindings()
|
||||
|
@ -95,18 +97,18 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
|
||||
keymap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
|
||||
keymap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(2u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::UnbindKeybindings()
|
||||
|
@ -125,52 +127,57 @@ namespace SettingsModelLocalTests
|
|||
const auto bindings4Json = VerifyParseSucceeded(bindings4String);
|
||||
const auto bindings5Json = VerifyParseSucceeded(bindings5String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
|
||||
keymap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using `\"unbound\"` to unbind the key"));
|
||||
keymap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using `null` to unbind the key"));
|
||||
// First add back a good binding
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
// Then try layering in the bad setting
|
||||
keymap->LayerJson(bindings3Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings3Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using an unrecognized command to unbind the key"));
|
||||
// First add back a good binding
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
// Then try layering in the bad setting
|
||||
keymap->LayerJson(bindings4Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings4Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using a straight up invalid value to unbind the key"));
|
||||
// First add back a good binding
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
// Then try layering in the bad setting
|
||||
keymap->LayerJson(bindings5Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings5Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key that wasn't bound at all"));
|
||||
keymap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestArbitraryArgs()
|
||||
|
@ -194,17 +201,17 @@ namespace SettingsModelLocalTests
|
|||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(10u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(10u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` without args parses as Copy(SingleLine=false)"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
|
@ -215,7 +222,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` with args parses them correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
|
@ -226,7 +233,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` with args parses them correctly"));
|
||||
KeyChord kc{ false, true, true, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
|
@ -237,7 +244,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` without args parses as NewTab(Index=null)"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('T') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -249,7 +256,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` parses args correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('T') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -263,7 +270,7 @@ namespace SettingsModelLocalTests
|
|||
L"Verify that `newTab` with an index greater than the legacy "
|
||||
L"args afforded parses correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('Y') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -277,7 +284,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` ignores args it doesn't understand"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('B') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -289,7 +296,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` null as it's `args` parses as the default option"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('B') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -301,7 +308,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `adjustFontSize` with a positive delta parses args correctly"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -313,7 +320,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `adjustFontSize` with a negative delta parses args correctly"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -333,15 +340,15 @@ namespace SettingsModelLocalTests
|
|||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(4u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -350,7 +357,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -359,7 +366,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -368,7 +375,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -387,15 +394,15 @@ namespace SettingsModelLocalTests
|
|||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(3u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -404,7 +411,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -415,7 +422,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -432,15 +439,15 @@ namespace SettingsModelLocalTests
|
|||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
|
@ -461,15 +468,15 @@ namespace SettingsModelLocalTests
|
|||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(6u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(6u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_UP) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollUp, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollUpArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -478,7 +485,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_DOWN) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollDown, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollDownArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -487,7 +494,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_UP) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollUp, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollUpArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -496,7 +503,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_DOWN) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollDown, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollDownArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -505,7 +512,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_UP) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollUp, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollUpArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -515,7 +522,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_DOWN) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ScrollDown, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ScrollDownArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -526,10 +533,10 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "scrollDown", "rowsToScroll": -1 } }])" };
|
||||
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
|
||||
auto invalidKeyMap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(invalidKeyMap);
|
||||
VERIFY_ARE_EQUAL(0u, invalidKeyMap->_keyShortcuts.size());
|
||||
VERIFY_THROWS(invalidKeyMap->LayerJson(bindingsInvalidJson);, std::exception);
|
||||
auto invalidActionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(invalidActionMap);
|
||||
VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size());
|
||||
VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -542,15 +549,15 @@ namespace SettingsModelLocalTests
|
|||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(2u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_UP) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MoveTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<MoveTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -559,7 +566,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_DOWN) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MoveTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<MoveTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -568,17 +575,17 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": "moveTab" }])" };
|
||||
auto keyMapNoArgs = winrt::make_self<implementation::KeyMapping>();
|
||||
keyMapNoArgs->LayerJson(bindingsInvalidString);
|
||||
VERIFY_ARE_EQUAL(0u, keyMapNoArgs->_keyShortcuts.size());
|
||||
auto actionMapNoArgs = winrt::make_self<implementation::ActionMap>();
|
||||
actionMapNoArgs->LayerJson(bindingsInvalidString);
|
||||
VERIFY_ARE_EQUAL(0u, actionMapNoArgs->_KeyMap.size());
|
||||
}
|
||||
{
|
||||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "moveTab", "direction": "bad" } }])" };
|
||||
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
|
||||
auto invalidKeyMap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(invalidKeyMap);
|
||||
VERIFY_ARE_EQUAL(0u, invalidKeyMap->_keyShortcuts.size());
|
||||
VERIFY_THROWS(invalidKeyMap->LayerJson(bindingsInvalidJson);, std::exception);
|
||||
auto invalidActionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(invalidActionMap);
|
||||
VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size());
|
||||
VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -592,15 +599,15 @@ namespace SettingsModelLocalTests
|
|||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(3u, keymap->_keyShortcuts.size());
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_UP) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ToggleCommandPalette, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ToggleCommandPaletteArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -609,7 +616,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_UP) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ToggleCommandPalette, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ToggleCommandPaletteArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -618,7 +625,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_UP) };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::ToggleCommandPalette, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<ToggleCommandPaletteArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -628,10 +635,69 @@ namespace SettingsModelLocalTests
|
|||
{
|
||||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "commandPalette", "launchMode": "bad" } }])" };
|
||||
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
|
||||
auto invalidKeyMap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(invalidKeyMap);
|
||||
VERIFY_ARE_EQUAL(0u, invalidKeyMap->_keyShortcuts.size());
|
||||
VERIFY_THROWS(invalidKeyMap->LayerJson(bindingsInvalidJson);, std::exception);
|
||||
auto invalidActionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(invalidActionMap);
|
||||
VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size());
|
||||
VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestGetKeyBindingForAction()
|
||||
{
|
||||
const std::string bindings0String{ R"([ { "command": "closeWindow", "keys": "ctrl+a" } ])" };
|
||||
const std::string bindings1String{ R"([ { "command": { "action": "copy", "singleLine": true }, "keys": "ctrl+b" } ])" };
|
||||
const std::string bindings2String{ R"([ { "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+c" } ])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto VerifyKeyChordEquality = [](const KeyChord& expected, const KeyChord& actual) {
|
||||
if (expected)
|
||||
{
|
||||
VERIFY_IS_NOT_NULL(actual);
|
||||
VERIFY_ARE_EQUAL(expected.Modifiers(), actual.Modifiers());
|
||||
VERIFY_ARE_EQUAL(expected.Vkey(), actual.Vkey());
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_IS_NULL(actual);
|
||||
}
|
||||
};
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
Log::Comment(L"simple command: no args");
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CloseWindow) };
|
||||
VerifyKeyChordEquality({ KeyModifiers::Ctrl, static_cast<int32_t>('A') }, kbd);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"command with args");
|
||||
actionMap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
|
||||
auto args{ winrt::make_self<implementation::CopyTextArgs>() };
|
||||
args->SingleLine(true);
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CopyText, *args) };
|
||||
VerifyKeyChordEquality({ KeyModifiers::Ctrl, static_cast<int32_t>('B') }, kbd);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"command with new terminal args");
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
|
||||
|
||||
auto newTerminalArgs{ winrt::make_self<implementation::NewTerminalArgs>() };
|
||||
newTerminalArgs->ProfileIndex(0);
|
||||
auto args{ winrt::make_self<implementation::NewTabArgs>(*newTerminalArgs) };
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::NewTab, *args) };
|
||||
VerifyKeyChordEquality({ KeyModifiers::Ctrl, static_cast<int32_t>('C') }, kbd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace SettingsModelLocalTests
|
|||
TEST_METHOD(LayerProfileProperties);
|
||||
TEST_METHOD(LayerProfileIcon);
|
||||
TEST_METHOD(LayerProfilesOnArray);
|
||||
TEST_METHOD(DuplicateProfileTest);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
|
@ -307,4 +308,22 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(0).Name());
|
||||
}
|
||||
|
||||
void ProfileTests::DuplicateProfileTest()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name" : "profile0",
|
||||
"backgroundImage" : "some//path"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
|
||||
auto settings = winrt::make_self<implementation::CascadiaSettings>();
|
||||
|
||||
settings->_LayerOrCreateProfile(profile0Json);
|
||||
auto duplicatedProfile = settings->DuplicateProfile(*settings->_FindMatchingProfile(profile0Json));
|
||||
duplicatedProfile.Name(L"profile0");
|
||||
|
||||
const auto duplicatedJson = winrt::get_self<implementation::Profile>(duplicatedProfile)->ToJson();
|
||||
VERIFY_ARE_EQUAL(profile0Json, duplicatedJson);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace SettingsModelLocalTests
|
|||
TEST_METHOD(GlobalSettings);
|
||||
TEST_METHOD(Profile);
|
||||
TEST_METHOD(ColorScheme);
|
||||
TEST_METHOD(Actions);
|
||||
TEST_METHOD(CascadiaSettings);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
|
@ -106,12 +107,15 @@ namespace SettingsModelLocalTests
|
|||
|
||||
"experimental.input.forceVT": false,
|
||||
"experimental.rendering.forceFullRepaint": false,
|
||||
"experimental.rendering.software": false
|
||||
"experimental.rendering.software": false,
|
||||
|
||||
"actions": []
|
||||
})" };
|
||||
|
||||
const std::string smallGlobalsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"actions": []
|
||||
})" };
|
||||
|
||||
RoundtripTest<implementation::GlobalAppSettings>(globalsString);
|
||||
|
@ -178,7 +182,9 @@ namespace SettingsModelLocalTests
|
|||
// - null should be acceptable even though we're working with colors
|
||||
const std::string weirdProfileString{ R"(
|
||||
{
|
||||
"guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}",
|
||||
"name": "Weird Profile",
|
||||
"hidden": false,
|
||||
"tabColor": null,
|
||||
"foreground": null,
|
||||
"source": "local"
|
||||
|
@ -221,6 +227,149 @@ namespace SettingsModelLocalTests
|
|||
RoundtripTest<implementation::ColorScheme>(schemeString);
|
||||
}
|
||||
|
||||
void SerializationTests::Actions()
|
||||
{
|
||||
const std::string actionsString1{ R"([
|
||||
{ "command": "paste" }
|
||||
])" };
|
||||
|
||||
const std::string actionsString2A{ R"([
|
||||
{ "command": { "action": "setTabColor" } }
|
||||
])" };
|
||||
const std::string actionsString2B{ R"([
|
||||
{ "command": { "action": "setTabColor", "color": "#112233" } }
|
||||
])" };
|
||||
const std::string actionsString2C{ R"([
|
||||
{ "command": { "action": "copy" } },
|
||||
{ "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" } }
|
||||
])" };
|
||||
|
||||
const std::string actionsString3{ R"([
|
||||
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+a" },
|
||||
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+b" }
|
||||
])" };
|
||||
|
||||
const std::string actionsString4{ R"([
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" }
|
||||
])" };
|
||||
|
||||
const std::string actionsString5{ R"([
|
||||
{ "icon": "image.png", "name": "Scroll To Top Name", "command": "scrollToTop", "keys": "ctrl+e" },
|
||||
{ "command": "scrollToTop", "keys": "ctrl+f" }
|
||||
])" };
|
||||
|
||||
const std::string actionsString6{ R"([
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+g" },
|
||||
])" };
|
||||
|
||||
const std::string actionsString7{ R"([
|
||||
{ "command": { "action": "renameWindow", "name": null }, "keys": "ctrl+h" }
|
||||
])" };
|
||||
|
||||
const std::string actionsString8{ R"([
|
||||
{
|
||||
"name": "Change font size...",
|
||||
"commands": [
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 } },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 } },
|
||||
{ "command": "resetFontSize" },
|
||||
]
|
||||
}
|
||||
])" };
|
||||
|
||||
const std::string actionsString9A{ R"([
|
||||
{
|
||||
"name": "New tab",
|
||||
"commands": [
|
||||
{
|
||||
"iterateOn": "profiles",
|
||||
"icon": "${profile.icon}",
|
||||
"name": "${profile.name}",
|
||||
"command": { "action": "newTab", "profile": "${profile.name}" }
|
||||
}
|
||||
]
|
||||
}
|
||||
])" };
|
||||
const std::string actionsString9B{ R"([
|
||||
{
|
||||
"commands":
|
||||
[
|
||||
{
|
||||
"command":
|
||||
{
|
||||
"action": "sendInput",
|
||||
"input": "${profile.name}"
|
||||
},
|
||||
"iterateOn": "profiles"
|
||||
}
|
||||
],
|
||||
"name": "Send Input ..."
|
||||
}
|
||||
])" };
|
||||
const std::string actionsString9C{ R""([
|
||||
{
|
||||
"commands":
|
||||
[
|
||||
{
|
||||
"commands":
|
||||
[
|
||||
{
|
||||
"command":
|
||||
{
|
||||
"action": "sendInput",
|
||||
"input": "${profile.name} ${scheme.name}"
|
||||
},
|
||||
"iterateOn": "schemes"
|
||||
}
|
||||
],
|
||||
"iterateOn": "profiles",
|
||||
"name": "nest level (${profile.name})"
|
||||
}
|
||||
],
|
||||
"name": "Send Input (Evil) ..."
|
||||
}
|
||||
])"" };
|
||||
|
||||
const std::string actionsString10{ R"([
|
||||
{ "command": "unbound", "keys": "ctrl+c" }
|
||||
])" };
|
||||
|
||||
Log::Comment(L"simple command");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString1);
|
||||
|
||||
Log::Comment(L"complex commands");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString2A);
|
||||
RoundtripTest<implementation::ActionMap>(actionsString2B);
|
||||
RoundtripTest<implementation::ActionMap>(actionsString2C);
|
||||
|
||||
Log::Comment(L"simple command with key chords");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString3);
|
||||
|
||||
Log::Comment(L"complex commands with key chords");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString4);
|
||||
|
||||
Log::Comment(L"command with name and icon and multiple key chords");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString5);
|
||||
|
||||
Log::Comment(L"complex command with new terminal args");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString6);
|
||||
|
||||
Log::Comment(L"complex command with meaningful null arg");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString7);
|
||||
|
||||
Log::Comment(L"nested command");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString8);
|
||||
|
||||
Log::Comment(L"iterable command");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString9A);
|
||||
RoundtripTest<implementation::ActionMap>(actionsString9B);
|
||||
RoundtripTest<implementation::ActionMap>(actionsString9C);
|
||||
|
||||
Log::Comment(L"unbound command");
|
||||
RoundtripTest<implementation::ActionMap>(actionsString10);
|
||||
}
|
||||
|
||||
void SerializationTests::CascadiaSettings()
|
||||
{
|
||||
const std::string settingsString{ R"({
|
||||
|
@ -277,10 +426,9 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
],
|
||||
"actions": [
|
||||
{"command": { "action": "renameTab","input": "Liang Tab" },"keys": "ctrl+t" }
|
||||
],
|
||||
"keybindings": [
|
||||
{ "command": { "action": "sendInput","input": "VT Griese Mode" },"keys": "ctrl+k" }
|
||||
{ "command": { "action": "renameTab", "title": "Liang Tab" }, "keys": "ctrl+t" },
|
||||
{ "command": { "action": "sendInput", "input": "VT Griese Mode" }, "keys": "ctrl+k" },
|
||||
{ "command": { "action": "renameWindow", "name": "Hecker Window" }, "keys": "ctrl+l" }
|
||||
]
|
||||
})" };
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
<ProjectName>LocalTests_SettingsModel</ProjectName>
|
||||
<TargetName>SettingsModel.LocalTests</TargetName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<WindowsTargetPlatformMinVersion>10.0.18362.0</WindowsTargetPlatformMinVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<OpenConsoleCppWinRTProject>true</OpenConsoleCppWinRTProject>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -55,6 +53,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\types\lib\types.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\propslib\propslib.vcxproj" />
|
||||
|
||||
<!-- If you don't reference these projects here, the
|
||||
_ConsoleGenerateAdditionalWinmdManifests step won't gather the winmd's -->
|
||||
|
@ -76,6 +75,13 @@
|
|||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<!--
|
||||
SettingsModelLib contains a DllMain that we need to force the use of.
|
||||
If you don't have this, then you'll see an error like
|
||||
"(init.obj) : error LNK2005: DllMain already defined in MSVCRTD.lib(dll_dllmain_stub.obj)"
|
||||
-->
|
||||
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12</AdditionalOptions>
|
||||
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
|
|
|
@ -103,17 +103,18 @@ namespace SettingsModelLocalTests
|
|||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
|
||||
auto keymap = settings.GlobalSettings().KeyMap();
|
||||
auto actionMap = settings.GlobalSettings().ActionMap();
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
const auto profile2Guid = settings.ActiveProfiles().GetAt(2).Guid();
|
||||
VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
|
||||
|
||||
VERIFY_ARE_EQUAL(12u, keymap.Size());
|
||||
const auto& actionMapImpl{ winrt::get_self<implementation::ActionMap>(actionMap) };
|
||||
VERIFY_ARE_EQUAL(12u, actionMapImpl->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -134,7 +135,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('B') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -156,7 +157,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -178,7 +179,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -200,7 +201,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -222,7 +223,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -245,7 +246,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -265,7 +266,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -287,7 +288,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('I') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -310,7 +311,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('J') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -332,7 +333,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('K') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
@ -355,7 +356,7 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('L') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
|
|
|
@ -19,11 +19,11 @@ public:
|
|||
// - This is a helper to retrieve the ActionAndArgs from the keybindings
|
||||
// for a given chord.
|
||||
// Arguments:
|
||||
// - keymap: The AppKeyBindings to lookup the ActionAndArgs from.
|
||||
// - actionMap: The ActionMap to lookup the ActionAndArgs from.
|
||||
// - kc: The key chord to look up the bound ActionAndArgs for.
|
||||
// Return Value:
|
||||
// - The ActionAndArgs bound to the given key, or nullptr if nothing is bound to it.
|
||||
static const winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs GetActionAndArgs(const winrt::Microsoft::Terminal::Settings::Model::KeyMapping& keymap,
|
||||
static const winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs GetActionAndArgs(const winrt::Microsoft::Terminal::Settings::Model::ActionMap& actionMap,
|
||||
const winrt::Microsoft::Terminal::Control::KeyChord& kc)
|
||||
{
|
||||
std::wstring buffer{ L"" };
|
||||
|
@ -42,8 +42,8 @@ public:
|
|||
buffer += static_cast<wchar_t>(MapVirtualKeyW(kc.Vkey(), MAPVK_VK_TO_CHAR));
|
||||
WEX::Logging::Log::Comment(WEX::Common::NoThrowString().Format(L"Looking for key:%s", buffer.c_str()));
|
||||
|
||||
const auto action = keymap.TryLookup(kc);
|
||||
VERIFY_IS_NOT_NULL(action, L"Expected to find an action bound to the given KeyChord");
|
||||
return action;
|
||||
const auto cmd = actionMap.GetActionByKeyChord(kc);
|
||||
VERIFY_IS_NOT_NULL(cmd, L"Expected to find an action bound to the given KeyChord");
|
||||
return cmd.ActionAndArgs();
|
||||
};
|
||||
};
|
||||
|
|
|
@ -57,6 +57,7 @@ namespace TerminalAppLocalTests
|
|||
TEST_METHOD(ParseFocusTabArgs);
|
||||
TEST_METHOD(ParseMoveFocusArgs);
|
||||
TEST_METHOD(ParseArgumentsWithParsingTerminators);
|
||||
TEST_METHOD(ParseFocusPaneArgs);
|
||||
|
||||
TEST_METHOD(ParseNoCommandIsNewTab);
|
||||
|
||||
|
@ -1206,6 +1207,108 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::ParseFocusPaneArgs()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:useShortForm", L"{false, true}")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
INIT_TEST_PROPERTY(bool, useShortForm, L"If true, use `fp` instead of `focus-pane`");
|
||||
const wchar_t* subcommand = useShortForm ? L"fp" : L"focus-pane";
|
||||
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand };
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Just the subcommand, without a target, should fail."));
|
||||
|
||||
_buildCommandlinesExpectFailureHelper(appArgs, 1u, rawCommands);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"left" };
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"focus-pane without a target should fail."));
|
||||
_buildCommandlinesExpectFailureHelper(appArgs, 1u, rawCommands);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"1" };
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"focus-pane without a target should fail."));
|
||||
_buildCommandlinesExpectFailureHelper(appArgs, 1u, rawCommands);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"--target", L"0" };
|
||||
_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());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::FocusPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<FocusPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(0u, myArgs.Id());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"-t", L"100" };
|
||||
_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());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::FocusPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<FocusPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(100u, myArgs.Id());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"--target", L"-1" };
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"focus-pane with an invalid target should fail."));
|
||||
_buildCommandlinesExpectFailureHelper(appArgs, 1u, rawCommands);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"move-focus", L"left", L";", subcommand, L"-t", L"1" };
|
||||
_buildCommandlinesHelper(appArgs, 2u, rawCommands);
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, 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());
|
||||
{
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MoveFocus, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<MoveFocusArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Left, myArgs.FocusDirection());
|
||||
}
|
||||
{
|
||||
auto actionAndArgs = appArgs._startupActions.at(2);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::FocusPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<FocusPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(1u, myArgs.Id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::ValidateFirstCommandIsNewTab()
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
|
|
|
@ -120,13 +120,13 @@ namespace TerminalAppLocalTests
|
|||
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto commands = settings.GlobalSettings().Commands();
|
||||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
auto nameMap{ settings.ActionMap().NameMap() };
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"iterable command ${profile.name}");
|
||||
auto command = nameMap.TryLookup(L"iterable command ${profile.name}");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -141,7 +141,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
@ -150,7 +150,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -168,7 +168,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -186,7 +186,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -247,13 +247,13 @@ namespace TerminalAppLocalTests
|
|||
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto commands = settings.GlobalSettings().Commands();
|
||||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
auto nameMap{ settings.ActionMap().NameMap() };
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"Split pane, profile: ${profile.name}");
|
||||
auto command = nameMap.TryLookup(L"Split pane, profile: ${profile.name}");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -268,7 +268,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
@ -277,7 +277,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"Split pane, profile: profile0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -295,7 +295,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"Split pane, profile: profile1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -313,7 +313,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"Split pane, profile: profile2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -376,13 +376,13 @@ namespace TerminalAppLocalTests
|
|||
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto commands = settings.GlobalSettings().Commands();
|
||||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
auto nameMap{ settings.ActionMap().NameMap() };
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"iterable command ${profile.name}");
|
||||
auto command = nameMap.TryLookup(L"iterable command ${profile.name}");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -397,7 +397,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
@ -406,7 +406,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -424,7 +424,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile1\"");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -442,7 +442,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -513,8 +513,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto commands = settings.GlobalSettings().Commands();
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
@ -522,8 +521,9 @@ namespace TerminalAppLocalTests
|
|||
|
||||
auto rootCommand = expandedCommands.Lookup(L"Connect to ssh...");
|
||||
VERIFY_IS_NOT_NULL(rootCommand);
|
||||
auto rootActionAndArgs = rootCommand.Action();
|
||||
VERIFY_IS_NULL(rootActionAndArgs);
|
||||
auto rootActionAndArgs = rootCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(rootActionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, rootCommand.NestedCommands().Size());
|
||||
|
||||
|
@ -531,7 +531,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring commandName{ L"first.com" };
|
||||
auto command = rootCommand.NestedCommands().Lookup(commandName);
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
||||
VERIFY_IS_FALSE(command.HasNestedCommands());
|
||||
|
@ -540,7 +540,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring commandName{ L"second.com" };
|
||||
auto command = rootCommand.NestedCommands().Lookup(commandName);
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
||||
VERIFY_IS_FALSE(command.HasNestedCommands());
|
||||
|
@ -608,8 +608,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto commands = settings.GlobalSettings().Commands();
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
@ -617,23 +616,25 @@ namespace TerminalAppLocalTests
|
|||
|
||||
auto grandparentCommand = expandedCommands.Lookup(L"grandparent");
|
||||
VERIFY_IS_NOT_NULL(grandparentCommand);
|
||||
auto grandparentActionAndArgs = grandparentCommand.Action();
|
||||
VERIFY_IS_NULL(grandparentActionAndArgs);
|
||||
auto grandparentActionAndArgs = grandparentCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(grandparentActionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, grandparentActionAndArgs.Action());
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, grandparentCommand.NestedCommands().Size());
|
||||
|
||||
winrt::hstring parentName{ L"parent" };
|
||||
auto parent = grandparentCommand.NestedCommands().Lookup(parentName);
|
||||
VERIFY_IS_NOT_NULL(parent);
|
||||
auto parentActionAndArgs = parent.Action();
|
||||
VERIFY_IS_NULL(parentActionAndArgs);
|
||||
auto parentActionAndArgs = parent.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(parentActionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, parentActionAndArgs.Action());
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, parent.NestedCommands().Size());
|
||||
{
|
||||
winrt::hstring childName{ L"child1" };
|
||||
auto child = parent.NestedCommands().Lookup(childName);
|
||||
VERIFY_IS_NOT_NULL(child);
|
||||
auto childActionAndArgs = child.Action();
|
||||
auto childActionAndArgs = child.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(childActionAndArgs);
|
||||
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, childActionAndArgs.Action());
|
||||
|
@ -653,7 +654,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring childName{ L"child2" };
|
||||
auto child = parent.NestedCommands().Lookup(childName);
|
||||
VERIFY_IS_NOT_NULL(child);
|
||||
auto childActionAndArgs = child.Action();
|
||||
auto childActionAndArgs = child.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(childActionAndArgs);
|
||||
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, childActionAndArgs.Action());
|
||||
|
@ -731,8 +732,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto commands = settings.GlobalSettings().Commands();
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
@ -744,8 +744,9 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring commandName{ name + L"..." };
|
||||
auto command = expandedCommands.Lookup(commandName);
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
VERIFY_IS_NULL(actionAndArgs);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, actionAndArgs.Action());
|
||||
|
||||
VERIFY_IS_TRUE(command.HasNestedCommands());
|
||||
VERIFY_ARE_EQUAL(3u, command.NestedCommands().Size());
|
||||
|
@ -754,7 +755,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring childCommandName{ fmt::format(L"Split pane, profile: {}", name) };
|
||||
auto childCommand = command.NestedCommands().Lookup(childCommandName);
|
||||
VERIFY_IS_NOT_NULL(childCommand);
|
||||
auto childActionAndArgs = childCommand.Action();
|
||||
auto childActionAndArgs = childCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(childActionAndArgs);
|
||||
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
|
||||
|
@ -775,7 +776,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: horizontal, profile: {}", name) };
|
||||
auto childCommand = command.NestedCommands().Lookup(childCommandName);
|
||||
VERIFY_IS_NOT_NULL(childCommand);
|
||||
auto childActionAndArgs = childCommand.Action();
|
||||
auto childActionAndArgs = childCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(childActionAndArgs);
|
||||
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
|
||||
|
@ -796,7 +797,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: vertical, profile: {}", name) };
|
||||
auto childCommand = command.NestedCommands().Lookup(childCommandName);
|
||||
VERIFY_IS_NOT_NULL(childCommand);
|
||||
auto childActionAndArgs = childCommand.Action();
|
||||
auto childActionAndArgs = childCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(childActionAndArgs);
|
||||
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
|
||||
|
@ -868,8 +869,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto commands = settings.GlobalSettings().Commands();
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
@ -877,8 +877,9 @@ namespace TerminalAppLocalTests
|
|||
|
||||
auto rootCommand = expandedCommands.Lookup(L"New Tab With Profile...");
|
||||
VERIFY_IS_NOT_NULL(rootCommand);
|
||||
auto rootActionAndArgs = rootCommand.Action();
|
||||
VERIFY_IS_NULL(rootActionAndArgs);
|
||||
auto rootActionAndArgs = rootCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(rootActionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, rootCommand.NestedCommands().Size());
|
||||
|
||||
|
@ -887,7 +888,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring commandName{ fmt::format(L"New tab, profile: {}", name) };
|
||||
auto command = rootCommand.NestedCommands().Lookup(commandName);
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
|
@ -971,8 +972,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto commands = settings.GlobalSettings().Commands();
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
@ -980,8 +980,9 @@ namespace TerminalAppLocalTests
|
|||
|
||||
auto rootCommand = expandedCommands.Lookup(L"New Pane...");
|
||||
VERIFY_IS_NOT_NULL(rootCommand);
|
||||
auto rootActionAndArgs = rootCommand.Action();
|
||||
VERIFY_IS_NULL(rootActionAndArgs);
|
||||
auto rootActionAndArgs = rootCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(rootActionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, rootCommand.NestedCommands().Size());
|
||||
|
||||
|
@ -990,8 +991,9 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring commandName{ name + L"..." };
|
||||
auto command = rootCommand.NestedCommands().Lookup(commandName);
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
VERIFY_IS_NULL(actionAndArgs);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, actionAndArgs.Action());
|
||||
|
||||
VERIFY_IS_TRUE(command.HasNestedCommands());
|
||||
VERIFY_ARE_EQUAL(3u, command.NestedCommands().Size());
|
||||
|
@ -1001,7 +1003,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring childCommandName{ fmt::format(L"Split pane, profile: {}", name) };
|
||||
auto childCommand = command.NestedCommands().Lookup(childCommandName);
|
||||
VERIFY_IS_NOT_NULL(childCommand);
|
||||
auto childActionAndArgs = childCommand.Action();
|
||||
auto childActionAndArgs = childCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(childActionAndArgs);
|
||||
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
|
||||
|
@ -1022,7 +1024,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: horizontal, profile: {}", name) };
|
||||
auto childCommand = command.NestedCommands().Lookup(childCommandName);
|
||||
VERIFY_IS_NOT_NULL(childCommand);
|
||||
auto childActionAndArgs = childCommand.Action();
|
||||
auto childActionAndArgs = childCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(childActionAndArgs);
|
||||
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
|
||||
|
@ -1043,7 +1045,7 @@ namespace TerminalAppLocalTests
|
|||
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: vertical, profile: {}", name) };
|
||||
auto childCommand = command.NestedCommands().Lookup(childCommandName);
|
||||
VERIFY_IS_NOT_NULL(childCommand);
|
||||
auto childActionAndArgs = childCommand.Action();
|
||||
auto childActionAndArgs = childCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(childActionAndArgs);
|
||||
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
|
||||
|
@ -1113,13 +1115,13 @@ namespace TerminalAppLocalTests
|
|||
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto commands = settings.GlobalSettings().Commands();
|
||||
VERIFY_ARE_EQUAL(1u, commands.Size());
|
||||
auto nameMap{ settings.ActionMap().NameMap() };
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"iterable command ${scheme.name}");
|
||||
auto command = nameMap.TryLookup(L"iterable command ${scheme.name}");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -1134,7 +1136,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_ARE_EQUAL(L"${scheme.name}", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
// This is the same warning as above
|
||||
|
@ -1148,7 +1150,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command scheme_0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -1166,7 +1168,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command scheme_1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
@ -1184,7 +1186,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command scheme_2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.Action();
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
|
|
|
@ -88,6 +88,11 @@ namespace TerminalAppLocalTests
|
|||
TEST_METHOD(TestWindowRenameSuccessful);
|
||||
TEST_METHOD(TestWindowRenameFailure);
|
||||
|
||||
TEST_METHOD(TestControlSettingsHasParent);
|
||||
TEST_METHOD(TestPreviewCommitScheme);
|
||||
TEST_METHOD(TestPreviewDismissScheme);
|
||||
TEST_METHOD(TestPreviewSchemeWhilePreviewing);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
return true;
|
||||
|
@ -291,7 +296,7 @@ namespace TerminalAppLocalTests
|
|||
auto tab = page->_tabs.GetAt(0);
|
||||
auto tabImpl = page->_GetTerminalTabImpl(tab);
|
||||
page->_tabView.SelectedItem(tabImpl->TabViewItem());
|
||||
page->_UpdatedSelectedTab(0);
|
||||
page->_UpdatedSelectedTab(tab);
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
@ -578,6 +583,75 @@ namespace TerminalAppLocalTests
|
|||
"tabTitle" : "Profile 3",
|
||||
"historySize": 4
|
||||
}
|
||||
],
|
||||
"schemes":
|
||||
[
|
||||
{
|
||||
"name": "Campbell",
|
||||
"foreground": "#CCCCCC",
|
||||
"background": "#0C0C0C",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#0C0C0C",
|
||||
"red": "#C50F1F",
|
||||
"green": "#13A10E",
|
||||
"yellow": "#C19C00",
|
||||
"blue": "#0037DA",
|
||||
"purple": "#881798",
|
||||
"cyan": "#3A96DD",
|
||||
"white": "#CCCCCC",
|
||||
"brightBlack": "#767676",
|
||||
"brightRed": "#E74856",
|
||||
"brightGreen": "#16C60C",
|
||||
"brightYellow": "#F9F1A5",
|
||||
"brightBlue": "#3B78FF",
|
||||
"brightPurple": "#B4009E",
|
||||
"brightCyan": "#61D6D6",
|
||||
"brightWhite": "#F2F2F2"
|
||||
},
|
||||
{
|
||||
"name": "Vintage",
|
||||
"foreground": "#C0C0C0",
|
||||
"background": "#000000",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#000000",
|
||||
"red": "#800000",
|
||||
"green": "#008000",
|
||||
"yellow": "#808000",
|
||||
"blue": "#000080",
|
||||
"purple": "#800080",
|
||||
"cyan": "#008080",
|
||||
"white": "#C0C0C0",
|
||||
"brightBlack": "#808080",
|
||||
"brightRed": "#FF0000",
|
||||
"brightGreen": "#00FF00",
|
||||
"brightYellow": "#FFFF00",
|
||||
"brightBlue": "#0000FF",
|
||||
"brightPurple": "#FF00FF",
|
||||
"brightCyan": "#00FFFF",
|
||||
"brightWhite": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"name": "One Half Light",
|
||||
"foreground": "#383A42",
|
||||
"background": "#FAFAFA",
|
||||
"cursorColor": "#4F525D",
|
||||
"black": "#383A42",
|
||||
"red": "#E45649",
|
||||
"green": "#50A14F",
|
||||
"yellow": "#C18301",
|
||||
"blue": "#0184BC",
|
||||
"purple": "#A626A4",
|
||||
"cyan": "#0997B3",
|
||||
"white": "#FAFAFA",
|
||||
"brightBlack": "#4F525D",
|
||||
"brightRed": "#DF6C75",
|
||||
"brightGreen": "#98C379",
|
||||
"brightYellow": "#E4C07A",
|
||||
"brightBlue": "#61AFEF",
|
||||
"brightPurple": "#C577DD",
|
||||
"brightCyan": "#56B5C1",
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
|
@ -910,10 +984,10 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(L"Select the tabs from 0 to 3");
|
||||
RunOnUIThread([&page]() {
|
||||
page->_UpdatedSelectedTab(0);
|
||||
page->_UpdatedSelectedTab(1);
|
||||
page->_UpdatedSelectedTab(2);
|
||||
page->_UpdatedSelectedTab(3);
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(0));
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(1));
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(2));
|
||||
page->_UpdatedSelectedTab(page->_tabs.GetAt(3));
|
||||
});
|
||||
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
|
@ -1012,4 +1086,260 @@ namespace TerminalAppLocalTests
|
|||
L"The window name should not have changed, we should have rejected the change.");
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::TestControlSettingsHasParent()
|
||||
{
|
||||
Log::Comment(L"Ensure that when we create a control, it always has a parent TerminalSettings");
|
||||
|
||||
auto page = _commonSetup();
|
||||
VERIFY_IS_NOT_NULL(page);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& originalSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::TestPreviewCommitScheme()
|
||||
{
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly");
|
||||
|
||||
auto page = _commonSetup();
|
||||
VERIFY_IS_NOT_NULL(page);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& originalSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground());
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Emulate previewing the SetColorScheme action");
|
||||
SetColorSchemeArgs args{ L"Vintage" };
|
||||
page->_PreviewColorScheme(args);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& previewSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(previewSettings);
|
||||
|
||||
const auto& originalSettings = previewSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
Log::Comment(L"Color should be changed to the preview");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Emulate committing the SetColorScheme action");
|
||||
|
||||
SetColorSchemeArgs args{ L"Vintage" };
|
||||
page->_EndPreviewColorScheme();
|
||||
page->_HandleSetColorScheme(nullptr, ActionEventArgs{ args });
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& originalSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
const auto& grandparentSettings = originalSettings.GetParent();
|
||||
VERIFY_IS_NULL(grandparentSettings);
|
||||
|
||||
Log::Comment(L"Color should be changed");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(nullptr, page->_originalSettings);
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::TestPreviewDismissScheme()
|
||||
{
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then dismissed accordingly");
|
||||
|
||||
auto page = _commonSetup();
|
||||
VERIFY_IS_NOT_NULL(page);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& originalSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground());
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Emulate previewing the SetColorScheme action");
|
||||
SetColorSchemeArgs args{ L"Vintage" };
|
||||
page->_PreviewColorScheme(args);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& previewSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(previewSettings);
|
||||
|
||||
const auto& originalSettings = previewSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
Log::Comment(L"Color should be changed to the preview");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Emulate dismissing the SetColorScheme action");
|
||||
page->_EndPreviewColorScheme();
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& originalSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
const auto& grandparentSettings = originalSettings.GetParent();
|
||||
VERIFY_IS_NULL(grandparentSettings);
|
||||
|
||||
Log::Comment(L"Color should be the same as it originally was");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(nullptr, page->_originalSettings);
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::TestPreviewSchemeWhilePreviewing()
|
||||
{
|
||||
Log::Comment(L"Preview a color scheme, then preview another scheme. ");
|
||||
|
||||
Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly");
|
||||
|
||||
auto page = _commonSetup();
|
||||
VERIFY_IS_NOT_NULL(page);
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& originalSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground());
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Emulate previewing the SetColorScheme action");
|
||||
SetColorSchemeArgs args{ L"Vintage" };
|
||||
page->_PreviewColorScheme(args);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& previewSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(previewSettings);
|
||||
|
||||
const auto& originalSettings = previewSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
Log::Comment(L"Color should be changed to the preview");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Now, preview another scheme");
|
||||
SetColorSchemeArgs args{ L"One Half Light" };
|
||||
page->_PreviewColorScheme(args);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& previewSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(previewSettings);
|
||||
|
||||
const auto& originalSettings = previewSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
Log::Comment(L"Color should be changed to the preview");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Emulate committing the SetColorScheme action");
|
||||
|
||||
SetColorSchemeArgs args{ L"One Half Light" };
|
||||
page->_EndPreviewColorScheme();
|
||||
page->_HandleSetColorScheme(nullptr, ActionEventArgs{ args });
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
const auto& activeControl{ page->_GetActiveControl() };
|
||||
VERIFY_IS_NOT_NULL(activeControl);
|
||||
|
||||
const auto& controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
VERIFY_IS_NOT_NULL(controlSettings);
|
||||
|
||||
const auto& originalSettings = controlSettings.GetParent();
|
||||
VERIFY_IS_NOT_NULL(originalSettings);
|
||||
|
||||
const auto& grandparentSettings = originalSettings.GetParent();
|
||||
VERIFY_IS_NULL(grandparentSettings);
|
||||
|
||||
Log::Comment(L"Color should be changed");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(nullptr, page->_originalSettings);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.18362.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
</Dependencies>
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<!-- Windows 10 1903 -->
|
||||
<!-- See https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/xaml-islands -->
|
||||
<!-- "maxversiontested" is CASE SENSITIVE. Do not change this.-->
|
||||
<!-- DO NOT ADVANCE PAST 18362. The OS has a bug where it won't recognize 19041 as bigger. -->
|
||||
<!-- This will cause unpackaged activation failures in XAML Islands. -->
|
||||
<!-- (We use unpackaged activation in test scenarios.) See GH#10265. -->
|
||||
<maxversiontested Id="10.0.18362.0"/>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
<ProjectName>LocalTests_TerminalApp</ProjectName>
|
||||
<TargetName>TerminalApp.LocalTests</TargetName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<WindowsTargetPlatformMinVersion>10.0.18362.0</WindowsTargetPlatformMinVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<OpenConsoleCppWinRTProject>true</OpenConsoleCppWinRTProject>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
<uap:SupportedUsers>multiple</uap:SupportedUsers>
|
||||
</Properties>
|
||||
<Dependencies>
|
||||
<!-- We're manually setting the version here, because the CI only runs windows 17763 -->
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.17763.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<WindowsStoreApp>true</WindowsStoreApp>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<WindowsTargetPlatformMinVersion>10.0.18362.0</WindowsTargetPlatformMinVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<OutputSubDir>Tests\Data</OutputSubDir>
|
||||
<UseWmXml>true</UseWmXml>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
<ProjectName>Microsoft.Terminal.Remoting.Lib</ProjectName>
|
||||
<RootNamespace>Microsoft.Terminal.Remoting</RootNamespace>
|
||||
<TargetName>Microsoft.Terminal.Remoting.Lib</TargetName>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
|
||||
|
@ -25,6 +24,12 @@
|
|||
<ClInclude Include="ProposeCommandlineResult.h">
|
||||
<DependentUpon>Monarch.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SummonWindowSelectionArgs.h">
|
||||
<DependentUpon>Monarch.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SummonWindowBehavior.h">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RenameRequestArgs.h">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -54,6 +59,12 @@
|
|||
<ClCompile Include="ProposeCommandlineResult.cpp">
|
||||
<DependentUpon>Monarch.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SummonWindowSelectionArgs.cpp">
|
||||
<DependentUpon>Monarch.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SummonWindowBehavior.cpp">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RenameRequestArgs.cpp">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
|
|
@ -84,14 +84,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
"Monarch_AddPeasant",
|
||||
TraceLoggingUInt64(providedID, "providedID", "the provided ID for the peasant"),
|
||||
TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return newPeasantsId;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_AddPeasant_Failed",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
// We can only get into this try/catch if the peasant died on us. So
|
||||
// the return value doesn't _really_ matter. They're not about to
|
||||
|
@ -234,7 +236,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_HandleActivatePeasant_Failed",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,7 +263,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
"Monarch_RemovedPeasantFromDesktop",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"),
|
||||
TraceLoggingGuid(result->DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
_mruPeasants.erase(result);
|
||||
}
|
||||
|
@ -297,7 +301,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingUInt64(localArgs->PeasantID(), "peasantID", "the ID of the activated peasant"),
|
||||
TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"),
|
||||
TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided localArgs->ActivatedTime()"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -306,9 +311,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// Arguments:
|
||||
// - limitToCurrentDesktop: if true, only return the MRU peasant that's
|
||||
// actually on the current desktop.
|
||||
// - ignoreQuakeWindow: if true, then don't return the _quake window when we
|
||||
// find it. This allows us to change our behavior for glomming vs
|
||||
// summoning. When summoning the window, this parameter should be true.
|
||||
// When glomming, this should be false, as to prevent glomming to the
|
||||
// _quake window.
|
||||
// Return Value:
|
||||
// - the ID of the most recent peasant, otherwise 0 if we could not find one.
|
||||
uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop)
|
||||
uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop, const bool ignoreQuakeWindow)
|
||||
{
|
||||
if (_mruPeasants.empty())
|
||||
{
|
||||
|
@ -334,7 +344,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_getMostRecentPeasantID_NoPeasants",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -368,14 +379,21 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
"peasantID",
|
||||
"We thought this peasant was the MRU one, but it was actually already dead."),
|
||||
TraceLoggingGuid(mruWindowArgs.DesktopID(), "desktopGuid", "The GUID of the desktop the window is on"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
// We'll go through the loop again. We removed the current one
|
||||
// at positionInList, so the next one in positionInList will be
|
||||
// a new, different peasant.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (limitToCurrentDesktop && _desktopManager)
|
||||
if (ignoreQuakeWindow && peasant.WindowName() == QuakeWindowName)
|
||||
{
|
||||
// The _quake window should never be treated as the MRU window.
|
||||
// Skip it if we see it. Users can still target it with `wt -w
|
||||
// _quake`, which will hit `_lookupPeasantIdForName` instead.
|
||||
}
|
||||
else if (limitToCurrentDesktop && _desktopManager)
|
||||
{
|
||||
// Check if this peasant is actually on this desktop. We can't
|
||||
// simply get the GUID of the current desktop. We have to ask if
|
||||
|
@ -401,7 +419,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingBool(onCurrentDesktop,
|
||||
"onCurrentDesktop",
|
||||
"true if this window was in fact on the current desktop"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return mruWindowArgs.PeasantID();
|
||||
}
|
||||
// If this window wasn't on the current desktop, another one
|
||||
|
@ -413,7 +432,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_getMostRecentPeasantID_Found",
|
||||
TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "The ID of the MRU peasant"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
return mruWindowArgs.PeasantID();
|
||||
}
|
||||
|
@ -425,7 +445,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// will use this to create a new window.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_getMostRecentPeasantID_NotFound",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -460,7 +481,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_ProposeCommandline",
|
||||
TraceLoggingInt64(targetWindow, "targetWindow", "The window ID the args specified"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
// If there's a valid ID returned, then let's try and find the peasant
|
||||
// that goes with it. Alternatively, if we were given a magic windowing
|
||||
|
@ -481,10 +503,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// lookup to find the window that spawned this process (then
|
||||
// fall back to sameDesktop if we can't find a match). For now,
|
||||
// it's good enough to just try to find a match on this desktop.
|
||||
windowID = _getMostRecentPeasantID(true);
|
||||
//
|
||||
// GH#projects/5#card-60325142 - Don't try to glom to the quake window.
|
||||
windowID = _getMostRecentPeasantID(true, true);
|
||||
break;
|
||||
case WindowingBehaviorUseAnyExisting:
|
||||
windowID = _getMostRecentPeasantID(false);
|
||||
windowID = _getMostRecentPeasantID(false, true);
|
||||
break;
|
||||
case WindowingBehaviorUseName:
|
||||
windowID = _lookupPeasantIdForName(targetWindowName);
|
||||
|
@ -499,7 +523,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingInt64(windowID,
|
||||
"windowID",
|
||||
"The actual peasant ID we evaluated the window ID as"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
// If_getMostRecentPeasantID returns 0 above, then we couldn't find
|
||||
// a matching window for that style of windowing. _getPeasant will
|
||||
|
@ -538,7 +563,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingBoolean(!result->ShouldCreateWindow(),
|
||||
"succeeded",
|
||||
"true if we successfully dispatched the commandline to the peasant"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return *result;
|
||||
}
|
||||
else if (windowID > 0)
|
||||
|
@ -553,7 +579,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
"peasantID",
|
||||
"the ID of the peasant the commandline waws intended for"),
|
||||
TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
auto result{ winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true) };
|
||||
result->Id(windowID);
|
||||
|
@ -566,7 +593,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_ProposeCommandline_NewWindow",
|
||||
TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
// In this case, no usable ID was provided. Return { true, nullopt }
|
||||
auto result = winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true);
|
||||
|
@ -628,7 +656,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_identifyWindows_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
_forAllPeasantsIgnoringTheDead(callback, onError);
|
||||
}
|
||||
|
@ -668,7 +697,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
"Monarch_renameRequested",
|
||||
TraceLoggingWideString(name.c_str(), "name", "The newly proposed name"),
|
||||
TraceLoggingInt64(successfullyRenamed, "successfullyRenamed", "true if the peasant is allowed to rename themselves to that name."),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
@ -677,7 +707,59 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// they're the only one who cares about the result.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_renameRequested_Failed",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to summon a window. `args` contains information about which
|
||||
// window we should try to summon:
|
||||
// * if a WindowName is provided, we'll try to find a window with exactly
|
||||
// that name, and fail if there isn't one.
|
||||
// - Calls Peasant::Summon on the matching peasant (which might be an RPC call)
|
||||
// - This should only ever be called by the WindowManager in the monarch
|
||||
// process itself. The monarch is the one registering for global hotkeys,
|
||||
// so it's the one calling this method.
|
||||
// Arguments:
|
||||
// - args: contains information about the window that should be summoned.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
// - Sets args.FoundMatch when a window matching args is found successfully.
|
||||
void Monarch::SummonWindow(const Remoting::SummonWindowSelectionArgs& args)
|
||||
{
|
||||
const auto searchedForName{ args.WindowName() };
|
||||
try
|
||||
{
|
||||
args.FoundMatch(false);
|
||||
uint64_t windowId = 0;
|
||||
// If no name was provided, then just summon the MRU window.
|
||||
if (searchedForName.empty())
|
||||
{
|
||||
// Use the value of the `desktop` arg to determine if we should
|
||||
// limit to the current desktop (desktop:onCurrent) or not
|
||||
// (desktop:any or desktop:toCurrent)
|
||||
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find a peasant that currently has this name
|
||||
windowId = _lookupPeasantIdForName(searchedForName);
|
||||
}
|
||||
if (auto targetPeasant{ _getPeasant(windowId) })
|
||||
{
|
||||
targetPeasant.Summon(args.SummonBehavior());
|
||||
args.FoundMatch(true);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonWindow_Failed",
|
||||
TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue