mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-09 15:51:05 +00:00
Compare commits
159 Commits
release-1.
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
595721658a | ||
|
|
306ad30753 | ||
|
|
00c7647594 | ||
|
|
97b0f06504 | ||
|
|
c2fcdcbe10 | ||
|
|
3d7480e9b7 | ||
|
|
97722d3efe | ||
|
|
a900ababdc | ||
|
|
54ed295588 | ||
|
|
bee6fb4368 | ||
|
|
6140fd9ab8 | ||
|
|
13e9546bab | ||
|
|
0a48836e83 | ||
|
|
43c76ee240 | ||
|
|
424414ec97 | ||
|
|
4f6f3b98b8 | ||
|
|
6268a4779c | ||
|
|
13bc71de3c | ||
|
|
a0670cb6b3 | ||
|
|
7908164f9d | ||
|
|
8ffea2c177 | ||
|
|
e0853ae4cc | ||
|
|
c089ae0c57 | ||
|
|
8d81497eb7 | ||
|
|
717ea85c9f | ||
|
|
efea1e5bad | ||
|
|
871b8de74f | ||
|
|
e4c5e8bd2a | ||
|
|
1acfef60f6 | ||
|
|
de379cd043 | ||
|
|
7112f4e081 | ||
|
|
7423734a48 | ||
|
|
07dc0601f9 | ||
|
|
6f42367ab8 | ||
|
|
d2c72e5c25 | ||
|
|
92437d718f | ||
|
|
817f598e20 | ||
|
|
2c5a35f1be | ||
|
|
ea58e4036b | ||
|
|
ee8800c739 | ||
|
|
f7b0f7444a | ||
|
|
1b6e6bd6dd | ||
|
|
7b6df26411 | ||
|
|
f3cc4c0328 | ||
|
|
f3a49fafe3 | ||
|
|
15c02b77a0 | ||
|
|
2c3368f766 | ||
|
|
b1131263cf | ||
|
|
c53fe1c2bf | ||
|
|
f9a844dbda | ||
|
|
23a19c5818 | ||
|
|
7712104983 | ||
|
|
608a49e817 | ||
|
|
10992b77a0 | ||
|
|
f6f5598c9c | ||
|
|
f681d3a1c1 | ||
|
|
d07546a6fe | ||
|
|
ed7c716978 | ||
|
|
0c901edd81 | ||
|
|
acf1ddc9c4 | ||
|
|
cb2f347c2f | ||
|
|
49874d1b9e | ||
|
|
cfdf03c24b | ||
|
|
70d44c84c8 | ||
|
|
1678b58dde | ||
|
|
482dcec60a | ||
|
|
46fd7caf5a | ||
|
|
638c6d0291 | ||
|
|
68294f863d | ||
|
|
59f184aa2d | ||
|
|
a544f56e17 | ||
|
|
29be8564f6 | ||
|
|
5d36e5d2df | ||
|
|
0220f71883 | ||
|
|
70560a789c | ||
|
|
a0edb12cd6 | ||
|
|
d3f9859051 | ||
|
|
f1dc649135 | ||
|
|
9eb9bc9235 | ||
|
|
d465a47bc5 | ||
|
|
9c858cd5b8 | ||
|
|
42bf605e1c | ||
|
|
121fb739fd | ||
|
|
ebf41dd6b2 | ||
|
|
a14b6f89f6 | ||
|
|
c55888f88d | ||
|
|
7acec306a6 | ||
|
|
cd4aabda84 | ||
|
|
fdffa24a71 | ||
|
|
9f2d40614b | ||
|
|
90ff261c35 | ||
|
|
dcbf7c74f1 | ||
|
|
76793b1e3f | ||
|
|
0b4839d94d | ||
|
|
2bd4670100 | ||
|
|
2eb659717c | ||
|
|
aea725f885 | ||
|
|
8ab3422b57 | ||
|
|
cccaab8545 | ||
|
|
e7108332f7 | ||
|
|
9ba20805ec | ||
|
|
94166942cc | ||
|
|
a2a605050f | ||
|
|
6936ee15fe | ||
|
|
a151607c79 | ||
|
|
fc64ff3029 | ||
|
|
34a6b1913c | ||
|
|
4b45bb8df1 | ||
|
|
f058b08fde | ||
|
|
b1bcc59230 | ||
|
|
10222a2ba2 | ||
|
|
3f5f37d910 | ||
|
|
37e0614554 | ||
|
|
d43a14c63f | ||
|
|
862217b04b | ||
|
|
3a71ead757 | ||
|
|
20e88d3e3e | ||
|
|
3ffaa1714a | ||
|
|
4c16cb278e | ||
|
|
335f69e099 | ||
|
|
cf97a9f772 | ||
|
|
41ade2c57e | ||
|
|
d1f152adcf | ||
|
|
8779249b12 | ||
|
|
10b12ac90c | ||
|
|
6ce2543a94 | ||
|
|
5a5902d580 | ||
|
|
0fefdac414 | ||
|
|
79115e2058 | ||
|
|
9c1331ab2e | ||
|
|
5f2ac4e3e7 | ||
|
|
6e70c4ae07 | ||
|
|
b05a557f48 | ||
|
|
2e246123cf | ||
|
|
fb69aecb19 | ||
|
|
730d6960ab | ||
|
|
7f3bc3cb04 | ||
|
|
8947909121 | ||
|
|
293c36d42f | ||
|
|
cf8f411b8f | ||
|
|
ee6ca81d70 | ||
|
|
b609266d29 | ||
|
|
59e2835736 | ||
|
|
3bf5436122 | ||
|
|
5ea778b2b8 | ||
|
|
cabb83db61 | ||
|
|
769e910e35 | ||
|
|
1d02f82ab7 | ||
|
|
84e30bcd3a | ||
|
|
f68324cd09 | ||
|
|
fca87b2bb5 | ||
|
|
c12835783d | ||
|
|
56bbe86f96 | ||
|
|
d13c37cd60 | ||
|
|
32fbd4cbb6 | ||
|
|
6d7723e3be | ||
|
|
e2005ca5d7 | ||
|
|
448684a5f4 | ||
|
|
0d3f85de85 |
15
.github/actions/spelling/README.md
vendored
Normal file
15
.github/actions/spelling/README.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# check-spelling/check-spelling configuration
|
||||
|
||||
File | Purpose | Format | Info
|
||||
-|-|-|-
|
||||
[allow/*.txt](allow/) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow)
|
||||
[reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject)
|
||||
[excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
|
||||
[patterns/*.txt](patterns/) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns)
|
||||
[line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[expect/*.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect)
|
||||
[advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice)
|
||||
|
||||
Note: you can replace any of these files with a directory by the same name (minus the suffix)
|
||||
and then include multiple files inside that directory (with that suffix) to merge multiple files together.
|
||||
38
.github/actions/spelling/advice.md
vendored
38
.github/actions/spelling/advice.md
vendored
@@ -1,4 +1,4 @@
|
||||
<!-- markdownlint-disable MD033 MD041 -->
|
||||
<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
|
||||
<details>
|
||||
<summary>
|
||||
:pencil2: Contributor please read this
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
|
||||
|
||||
: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...
|
||||
:warning: The command is written for posix shells. If it doesn't work for you, you can manually _add_ (one word per line) / _remove_ items to `expect.txt` and the `excludes.txt` files.
|
||||
|
||||
If the listed items are:
|
||||
|
||||
@@ -20,31 +20,29 @@ 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:
|
||||
|
||||
<details><summary>:clamp: If you see a bunch of garbage</summary>
|
||||
|
||||
If it relates to a ...
|
||||
<details><summary>well-formed pattern</summary>
|
||||
<details><summary>If the flagged items are :exploding_head: false positives</summary>
|
||||
|
||||
See if there's a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it.
|
||||
If items relate to a ...
|
||||
* binary file (or some other file you wouldn't want to check at all).
|
||||
|
||||
If not, try writing one and adding it to a `patterns/{file}.txt`.
|
||||
Please add a file path to the `excludes.txt` file matching the containing file.
|
||||
|
||||
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](
|
||||
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](
|
||||
`^` 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>
|
||||
|
||||
|
||||
* well-formed pattern.
|
||||
|
||||
If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
|
||||
try adding it to the `patterns.txt` file.
|
||||
|
||||
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>
|
||||
|
||||
50
.github/actions/spelling/allow/allow.txt
vendored
50
.github/actions/spelling/allow/allow.txt
vendored
@@ -1,7 +1,18 @@
|
||||
admins
|
||||
allcolors
|
||||
Apc
|
||||
apc
|
||||
breadcrumb
|
||||
breadcrumbs
|
||||
bsd
|
||||
calt
|
||||
ccmp
|
||||
changelog
|
||||
clickable
|
||||
clig
|
||||
CMMI
|
||||
copyable
|
||||
cybersecurity
|
||||
dalet
|
||||
Dcs
|
||||
dcs
|
||||
@@ -11,48 +22,87 @@ downside
|
||||
downsides
|
||||
dze
|
||||
dzhe
|
||||
EDDB
|
||||
EDDC
|
||||
Enum'd
|
||||
Fitt
|
||||
formattings
|
||||
FTCS
|
||||
ftp
|
||||
fvar
|
||||
gantt
|
||||
gcc
|
||||
geeksforgeeks
|
||||
ghe
|
||||
github
|
||||
gje
|
||||
godbolt
|
||||
hostname
|
||||
hostnames
|
||||
https
|
||||
hyperlink
|
||||
hyperlinking
|
||||
hyperlinks
|
||||
iconify
|
||||
img
|
||||
inlined
|
||||
It'd
|
||||
kje
|
||||
libfuzzer
|
||||
libuv
|
||||
liga
|
||||
lje
|
||||
Llast
|
||||
llvm
|
||||
Lmid
|
||||
locl
|
||||
lol
|
||||
lorem
|
||||
Lorigin
|
||||
maxed
|
||||
minimalistic
|
||||
mkmk
|
||||
mnt
|
||||
mru
|
||||
nje
|
||||
noreply
|
||||
ogonek
|
||||
ok'd
|
||||
overlined
|
||||
pipeline
|
||||
postmodern
|
||||
ptys
|
||||
qof
|
||||
qps
|
||||
rclt
|
||||
reimplementation
|
||||
reserialization
|
||||
reserialize
|
||||
reserializes
|
||||
rlig
|
||||
runtimes
|
||||
shcha
|
||||
slnt
|
||||
Sos
|
||||
ssh
|
||||
timeline
|
||||
timelines
|
||||
timestamped
|
||||
TLDR
|
||||
tokenizes
|
||||
tonos
|
||||
toolset
|
||||
tshe
|
||||
ubuntu
|
||||
uiatextrange
|
||||
UIs
|
||||
und
|
||||
unregister
|
||||
versioned
|
||||
vsdevcmd
|
||||
We'd
|
||||
wildcards
|
||||
XBox
|
||||
YBox
|
||||
yeru
|
||||
zhe
|
||||
|
||||
79
.github/actions/spelling/allow/apis.txt
vendored
79
.github/actions/spelling/allow/apis.txt
vendored
@@ -1,30 +1,48 @@
|
||||
ACCEPTFILES
|
||||
ACCESSDENIED
|
||||
acl
|
||||
aclapi
|
||||
alignas
|
||||
alignof
|
||||
APPLYTOSUBMENUS
|
||||
appxrecipe
|
||||
bitfield
|
||||
bitfields
|
||||
BUILDBRANCH
|
||||
BUILDMSG
|
||||
BUILDNUMBER
|
||||
BYCOMMAND
|
||||
BYPOSITION
|
||||
charconv
|
||||
CLASSNOTAVAILABLE
|
||||
CLOSEAPP
|
||||
cmdletbinding
|
||||
COLORPROPERTY
|
||||
colspan
|
||||
COMDLG
|
||||
commandlinetoargv
|
||||
comparand
|
||||
cstdint
|
||||
CXICON
|
||||
CYICON
|
||||
Dacl
|
||||
dataobject
|
||||
dcomp
|
||||
DERR
|
||||
dlldata
|
||||
DNE
|
||||
DONTADDTORECENT
|
||||
DWMSBT
|
||||
DWMWA
|
||||
DWMWA
|
||||
DWORDLONG
|
||||
endfor
|
||||
ENDSESSION
|
||||
enumset
|
||||
environstrings
|
||||
EXPCMDFLAGS
|
||||
EXPCMDSTATE
|
||||
filetime
|
||||
FILTERSPEC
|
||||
FORCEFILESYSTEM
|
||||
FORCEMINIMIZE
|
||||
@@ -33,12 +51,16 @@ fullkbd
|
||||
futex
|
||||
GETDESKWALLPAPER
|
||||
GETHIGHCONTRAST
|
||||
GETMOUSEHOVERTIME
|
||||
Hashtable
|
||||
HIGHCONTRASTON
|
||||
HIGHCONTRASTW
|
||||
hotkeys
|
||||
href
|
||||
hrgn
|
||||
HTCLOSE
|
||||
hwinsta
|
||||
HWINSTA
|
||||
IActivation
|
||||
IApp
|
||||
IAppearance
|
||||
@@ -55,17 +77,22 @@ IDirect
|
||||
IExplorer
|
||||
IFACEMETHOD
|
||||
IFile
|
||||
IGraphics
|
||||
IInheritable
|
||||
IMap
|
||||
IMonarch
|
||||
IObject
|
||||
iosfwd
|
||||
IPackage
|
||||
IPeasant
|
||||
ISetup
|
||||
isspace
|
||||
IStorage
|
||||
istream
|
||||
IStringable
|
||||
ITab
|
||||
ITaskbar
|
||||
itow
|
||||
IUri
|
||||
IVirtual
|
||||
KEYSELECT
|
||||
@@ -74,12 +101,27 @@ llabs
|
||||
llu
|
||||
localtime
|
||||
lround
|
||||
Lsa
|
||||
lsass
|
||||
LSHIFT
|
||||
LTGRAY
|
||||
MAINWINDOW
|
||||
memchr
|
||||
memicmp
|
||||
MENUCOMMAND
|
||||
MENUDATA
|
||||
MENUINFO
|
||||
MENUITEMINFOW
|
||||
mmeapi
|
||||
MOUSELEAVE
|
||||
mov
|
||||
mptt
|
||||
msappx
|
||||
MULTIPLEUSE
|
||||
NCHITTEST
|
||||
NCLBUTTONDBLCLK
|
||||
NCMOUSELEAVE
|
||||
NCMOUSEMOVE
|
||||
NCRBUTTONDBLCLK
|
||||
NIF
|
||||
NIN
|
||||
@@ -89,6 +131,7 @@ NOCHANGEDIR
|
||||
NOPROGRESS
|
||||
NOREDIRECTIONBITMAP
|
||||
NOREPEAT
|
||||
NOTIFYBYPOS
|
||||
NOTIFYICON
|
||||
NOTIFYICONDATA
|
||||
ntprivapi
|
||||
@@ -96,26 +139,36 @@ oaidl
|
||||
ocidl
|
||||
ODR
|
||||
offsetof
|
||||
ofstream
|
||||
onefuzz
|
||||
osver
|
||||
OSVERSIONINFOEXW
|
||||
otms
|
||||
OUTLINETEXTMETRICW
|
||||
overridable
|
||||
PACL
|
||||
PAGESCROLL
|
||||
PATINVERT
|
||||
PEXPLICIT
|
||||
PICKFOLDERS
|
||||
pmr
|
||||
ptstr
|
||||
QUERYENDSESSION
|
||||
rcx
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
ROOTOWNER
|
||||
roundf
|
||||
RSHIFT
|
||||
SACL
|
||||
schandle
|
||||
semver
|
||||
serializer
|
||||
SETVERSION
|
||||
SHELLEXECUTEINFOW
|
||||
shobjidl
|
||||
SHOWHIDE
|
||||
SHOWMINIMIZED
|
||||
SHOWTIP
|
||||
SINGLEUSE
|
||||
@@ -124,27 +177,49 @@ smoothstep
|
||||
snprintf
|
||||
spsc
|
||||
sregex
|
||||
SRWLOC
|
||||
SRWLOCK
|
||||
STDCPP
|
||||
STDMETHOD
|
||||
strchr
|
||||
strcpy
|
||||
streambuf
|
||||
strtoul
|
||||
Stubless
|
||||
Subheader
|
||||
Subpage
|
||||
syscall
|
||||
SYSTEMBACKDROP
|
||||
TABROW
|
||||
TASKBARCREATED
|
||||
TBPF
|
||||
THEMECHANGED
|
||||
tlg
|
||||
TME
|
||||
tmp
|
||||
tmpdir
|
||||
tolower
|
||||
toupper
|
||||
TRACKMOUSEEVENT
|
||||
TTask
|
||||
TVal
|
||||
UChar
|
||||
UFIELD
|
||||
ULARGE
|
||||
UOI
|
||||
UPDATEINIFILE
|
||||
userenv
|
||||
USEROBJECTFLAGS
|
||||
Viewbox
|
||||
virtualalloc
|
||||
wcsstr
|
||||
wcstoui
|
||||
winmain
|
||||
winsta
|
||||
winstamin
|
||||
wmemcmp
|
||||
wpc
|
||||
WSF
|
||||
wsregex
|
||||
wwinmain
|
||||
xchg
|
||||
@@ -152,6 +227,7 @@ XDocument
|
||||
XElement
|
||||
xfacet
|
||||
xhash
|
||||
XIcon
|
||||
xiosbase
|
||||
xlocale
|
||||
xlocbuf
|
||||
@@ -160,6 +236,7 @@ xlocmes
|
||||
xlocmon
|
||||
xlocnum
|
||||
xloctime
|
||||
XMax
|
||||
xmemory
|
||||
XParse
|
||||
xpath
|
||||
@@ -167,3 +244,5 @@ xstddef
|
||||
xstring
|
||||
xtree
|
||||
xutility
|
||||
YIcon
|
||||
YMax
|
||||
|
||||
1
.github/actions/spelling/allow/fonts.txt
vendored
1
.github/actions/spelling/allow/fonts.txt
vendored
@@ -7,3 +7,4 @@ Iosevka
|
||||
MDL
|
||||
Monofur
|
||||
Segoe
|
||||
wght
|
||||
|
||||
8
.github/actions/spelling/allow/math.txt
vendored
8
.github/actions/spelling/allow/math.txt
vendored
@@ -1,3 +1,11 @@
|
||||
atan
|
||||
CPrime
|
||||
HBar
|
||||
HPrime
|
||||
isnan
|
||||
LPrime
|
||||
LStep
|
||||
powf
|
||||
RSub
|
||||
sqrtf
|
||||
ULP
|
||||
|
||||
16
.github/actions/spelling/allow/microsoft.txt
vendored
16
.github/actions/spelling/allow/microsoft.txt
vendored
@@ -1,5 +1,6 @@
|
||||
ACLs
|
||||
ADMINS
|
||||
advapi
|
||||
altform
|
||||
altforms
|
||||
appendwttlogging
|
||||
@@ -15,8 +16,10 @@ CPLs
|
||||
cpptools
|
||||
cppvsdbg
|
||||
CPRs
|
||||
cryptbase
|
||||
DACL
|
||||
DACLs
|
||||
defaultlib
|
||||
diffs
|
||||
disposables
|
||||
dotnetfeed
|
||||
@@ -24,14 +27,22 @@ DTDs
|
||||
DWINRT
|
||||
enablewttlogging
|
||||
Intelli
|
||||
IVisual
|
||||
libucrt
|
||||
libucrtd
|
||||
LKG
|
||||
LOCKFILE
|
||||
Lxss
|
||||
mfcribbon
|
||||
microsoft
|
||||
microsoftonline
|
||||
MSAA
|
||||
msixbundle
|
||||
MSVC
|
||||
MSVCP
|
||||
muxc
|
||||
netcore
|
||||
Onefuzz
|
||||
osgvsowi
|
||||
PFILETIME
|
||||
pgc
|
||||
@@ -42,8 +53,10 @@ powershell
|
||||
propkey
|
||||
pscustomobject
|
||||
QWORD
|
||||
regedit
|
||||
robocopy
|
||||
SACLs
|
||||
sdkddkver
|
||||
Shobjidl
|
||||
Skype
|
||||
SRW
|
||||
@@ -54,6 +67,8 @@ systemroot
|
||||
taskkill
|
||||
tasklist
|
||||
tdbuildteamid
|
||||
ucrt
|
||||
ucrtd
|
||||
unvirtualized
|
||||
VCRT
|
||||
vcruntime
|
||||
@@ -61,6 +76,7 @@ Virtualization
|
||||
visualstudio
|
||||
vscode
|
||||
VSTHRD
|
||||
winsdkver
|
||||
wlk
|
||||
wslpath
|
||||
wtl
|
||||
|
||||
14
.github/actions/spelling/allow/names.txt
vendored
14
.github/actions/spelling/allow/names.txt
vendored
@@ -1,14 +1,18 @@
|
||||
Anup
|
||||
austdi
|
||||
arkthur
|
||||
Ballmer
|
||||
bhoj
|
||||
Bhojwani
|
||||
Bluloco
|
||||
carlos
|
||||
dhowett
|
||||
Diviness
|
||||
dsafa
|
||||
duhowett
|
||||
DXP
|
||||
ekg
|
||||
eryksun
|
||||
ethanschoonover
|
||||
Firefox
|
||||
Gatta
|
||||
@@ -20,6 +24,7 @@ Hernan
|
||||
Howett
|
||||
Illhardt
|
||||
iquilezles
|
||||
italo
|
||||
jantari
|
||||
jerrysh
|
||||
Kaiyu
|
||||
@@ -31,8 +36,11 @@ Kourosh
|
||||
kowalczyk
|
||||
leonmsft
|
||||
Lepilleur
|
||||
lhecker
|
||||
lukesampson
|
||||
Macbook
|
||||
Manandhar
|
||||
masserano
|
||||
mbadolato
|
||||
Mehrain
|
||||
menger
|
||||
@@ -52,6 +60,7 @@ oldnewthing
|
||||
opengl
|
||||
osgwiki
|
||||
pabhojwa
|
||||
panos
|
||||
paulcam
|
||||
pauldotknopf
|
||||
PGP
|
||||
@@ -60,12 +69,17 @@ Rincewind
|
||||
rprichard
|
||||
Schoonover
|
||||
shadertoy
|
||||
Shomnipotence
|
||||
simioni
|
||||
Somuah
|
||||
sonph
|
||||
sonpham
|
||||
stakx
|
||||
talo
|
||||
thereses
|
||||
Walisch
|
||||
WDX
|
||||
Wellons
|
||||
Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
|
||||
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
@@ -0,0 +1,523 @@
|
||||
# marker to ignore all code on line
|
||||
^.*/\* #no-spell-check-line \*/.*$
|
||||
# marker for ignoring a comment to the end of the line
|
||||
// #no-spell-check.*$
|
||||
|
||||
# patch hunk comments
|
||||
^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .*
|
||||
# git index header
|
||||
index [0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
|
||||
|
||||
# cid urls
|
||||
(['"])cid:.*?\g{-1}
|
||||
|
||||
# data url in parens
|
||||
\(data:[^)]*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\)
|
||||
# data url in quotes
|
||||
([`'"])data:.*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
|
||||
# data url
|
||||
data:[-a-zA-Z=;:/0-9+]*,\S*
|
||||
|
||||
# mailto urls
|
||||
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
|
||||
|
||||
# magnet urls
|
||||
magnet:[?=:\w]+
|
||||
|
||||
# magnet urls
|
||||
"magnet:[^"]+"
|
||||
|
||||
# obs:
|
||||
"obs:[^"]*"
|
||||
|
||||
# The `\b` here means a break, it's the fancy way to handle urls, but it makes things harder to read
|
||||
# In this examples content, I'm using a number of different ways to match things to show various approaches
|
||||
# asciinema
|
||||
\basciinema\.org/a/[0-9a-zA-Z]+
|
||||
|
||||
# apple
|
||||
\bdeveloper\.apple\.com/[-\w?=/]+
|
||||
# Apple music
|
||||
\bembed\.music\.apple\.com/fr/playlist/usr-share/[-\w.]+
|
||||
|
||||
# appveyor api
|
||||
\bci\.appveyor\.com/api/projects/status/[0-9a-z]+
|
||||
# appveyor project
|
||||
\bci\.appveyor\.com/project/(?:[^/\s"]*/){2}builds?/\d+/job/[0-9a-z]+
|
||||
|
||||
# Amazon
|
||||
|
||||
# Amazon
|
||||
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
|
||||
# AWS S3
|
||||
\b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS execute-api
|
||||
\b[0-9a-z]{10}\.execute-api\.[-0-9a-z]+\.amazonaws\.com\b
|
||||
# AWS ELB
|
||||
\b\w+\.[-0-9a-z]+\.elb\.amazonaws\.com\b
|
||||
# AWS SNS
|
||||
\bsns\.[-0-9a-z]+.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS VPC
|
||||
vpc-\w+
|
||||
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
|
||||
# YouTube music
|
||||
\bmusic\.youtube\.com/youtubei/v1/browse(?:[?&]\w+=[-a-zA-Z0-9?&=_]*)
|
||||
# YouTube tag
|
||||
<\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"]
|
||||
# YouTube image
|
||||
\bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]*
|
||||
# Google Accounts
|
||||
\baccounts.google.com/[-_/?=.:;+%&0-9a-zA-Z]*
|
||||
# Google Analytics
|
||||
\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
|
||||
# Google APIs
|
||||
\bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+
|
||||
# Google Storage
|
||||
\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
|
||||
# Google Calendar
|
||||
\bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+
|
||||
\w+\@group\.calendar\.google\.com\b
|
||||
# Google DataStudio
|
||||
\bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|)
|
||||
# The leading `/` here is as opposed to the `\b` above
|
||||
# ... a short way to match `https://` or `http://` since most urls have one of those prefixes
|
||||
# Google Docs
|
||||
/docs\.google\.com/[a-z]+/(?:ccc\?key=\w+|(?:u/\d+|d/(?:e/|)[0-9a-zA-Z_-]+/)?(?:edit\?[-\w=#.]*|/\?[\w=&]*|))
|
||||
# Google Drive
|
||||
\bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]*
|
||||
# Google Groups
|
||||
\bgroups\.google\.com/(?:(?:forum/#!|d/)(?:msg|topics?|searchin)|a)/[^/\s"]+/[-a-zA-Z0-9$]+(?:/[-a-zA-Z0-9]+)*
|
||||
# Google Maps
|
||||
\bmaps\.google\.com/maps\?[\w&;=]*
|
||||
# Google themes
|
||||
themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
|
||||
# Google CDN
|
||||
\bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]*
|
||||
# Goo.gl
|
||||
/goo\.gl/[a-zA-Z0-9]+
|
||||
# Google Chrome Store
|
||||
\bchrome\.google\.com/webstore/detail/[-\w]*(?:/\w*|)
|
||||
# Google Books
|
||||
\bgoogle\.(?:\w{2,4})/books(?:/\w+)*\?[-\w\d=&#.]*
|
||||
# Google Fonts
|
||||
\bfonts\.(?:googleapis|gstatic)\.com/[-/?=:;+&0-9a-zA-Z]*
|
||||
# Google Forms
|
||||
\bforms\.gle/\w+
|
||||
# Google Scholar
|
||||
\bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+
|
||||
# Google Colab Research Drive
|
||||
\bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]*
|
||||
|
||||
# GitHub SHAs (api)
|
||||
\bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b
|
||||
# GitHub SHAs (markdown)
|
||||
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
|
||||
# GitHub SHAs
|
||||
\bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b
|
||||
# GitHub wiki
|
||||
\bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b
|
||||
# githubusercontent
|
||||
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
# githubassets
|
||||
\bgithubassets.com/[0-9a-f]+(?:[-/\w.]+)
|
||||
# gist github
|
||||
\bgist\.github\.com/[^/\s"]+/[0-9a-f]+
|
||||
# git.io
|
||||
\bgit\.io/[0-9a-zA-Z]+
|
||||
# GitHub JSON
|
||||
"node_id": "[-a-zA-Z=;:/0-9+]*"
|
||||
# Contributor
|
||||
\[[^\]]+\]\(https://github\.com/[^/\s"]+\)
|
||||
# GHSA
|
||||
GHSA(?:-[0-9a-z]{4}){3}
|
||||
|
||||
# GitLab commit
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
|
||||
# GitLab merge requests
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/-/merge_requests/\d+/diffs#[0-9a-f]{40}\b
|
||||
# GitLab uploads
|
||||
\bgitlab\.[^/\s"]*/uploads/[-a-zA-Z=;:/0-9+]*
|
||||
# GitLab commits
|
||||
\bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b
|
||||
|
||||
# binanace
|
||||
accounts.binance.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
|
||||
|
||||
# bitbucket diff
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+
|
||||
# bitbucket repositories commits
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
# bitbucket commits
|
||||
\bbitbucket\.org/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
|
||||
# bit.ly
|
||||
\bbit\.ly/\w+
|
||||
|
||||
# bitrise
|
||||
\bapp\.bitrise\.io/app/[0-9a-f]*/[\w.?=&]*
|
||||
|
||||
# bootstrapcdn.com
|
||||
\bbootstrapcdn\.com/[-./\w]+
|
||||
|
||||
# cdn.cloudflare.com
|
||||
\bcdnjs\.cloudflare\.com/[./\w]+
|
||||
|
||||
# circleci
|
||||
\bcircleci\.com/gh(?:/[^/\s"]+){1,5}.[a-z]+\?[-0-9a-zA-Z=&]+
|
||||
|
||||
# gitter
|
||||
\bgitter\.im(?:/[^/\s"]+){2}\?at=[0-9a-f]+
|
||||
|
||||
# gravatar
|
||||
\bgravatar\.com/avatar/[0-9a-f]+
|
||||
|
||||
# ibm
|
||||
[a-z.]*ibm\.com/[-_#=:%!?~.\\/\d\w]*
|
||||
|
||||
# imgur
|
||||
\bimgur\.com/[^.]+
|
||||
|
||||
# Internet Archive
|
||||
\barchive\.org/web/\d+/(?:[-\w.?,'/\\+&%$#_:]*)
|
||||
|
||||
# discord
|
||||
/discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,}
|
||||
|
||||
# Disqus
|
||||
\bdisqus\.com/[-\w/%.()!?&=_]*
|
||||
|
||||
# medium link
|
||||
\blink\.medium\.com/[a-zA-Z0-9]+
|
||||
# medium
|
||||
\bmedium\.com/\@?[^/\s"]+/[-\w]+
|
||||
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
|
||||
# powerbi
|
||||
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
|
||||
# vs devops
|
||||
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
|
||||
# microsoft store
|
||||
\bmicrosoft\.com/store/apps/\w+
|
||||
|
||||
# mvnrepository.com
|
||||
\bmvnrepository\.com/[-0-9a-z./]+
|
||||
|
||||
# now.sh
|
||||
/[0-9a-z-.]+\.now\.sh\b
|
||||
|
||||
# oracle
|
||||
\bdocs\.oracle\.com/[-0-9a-zA-Z./_?#&=]*
|
||||
|
||||
# chromatic.com
|
||||
/\S+.chromatic.com\S*[")]
|
||||
|
||||
# codacy
|
||||
\bapi\.codacy\.com/project/badge/Grade/[0-9a-f]+
|
||||
|
||||
# compai
|
||||
\bcompai\.pub/v1/png/[0-9a-f]+
|
||||
|
||||
# mailgun api
|
||||
\.api\.mailgun\.net/v3/domains/[0-9a-z]+\.mailgun.org/messages/[0-9a-zA-Z=@]*
|
||||
# mailgun
|
||||
\b[0-9a-z]+.mailgun.org
|
||||
|
||||
# /message-id/
|
||||
/message-id/[-\w@./%]+
|
||||
|
||||
# Reddit
|
||||
\breddit\.com/r/[/\w_]*
|
||||
|
||||
# requestb.in
|
||||
\brequestb\.in/[0-9a-z]+
|
||||
|
||||
# sched
|
||||
\b[a-z0-9]+\.sched\.com\b
|
||||
|
||||
# Slack url
|
||||
slack://[a-zA-Z0-9?&=]+
|
||||
# Slack
|
||||
\bslack\.com/[-0-9a-zA-Z/_~?&=.]*
|
||||
# Slack edge
|
||||
\bslack-edge\.com/[-a-zA-Z0-9?&=%./]+
|
||||
# Slack images
|
||||
\bslack-imgs\.com/[-a-zA-Z0-9?&=%.]+
|
||||
|
||||
# shields.io
|
||||
\bshields\.io/[-\w/%?=&.:+;,]*
|
||||
|
||||
# stackexchange -- https://stackexchange.com/feeds/sites
|
||||
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
|
||||
|
||||
# Sentry
|
||||
[0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b
|
||||
|
||||
# Twitter markdown
|
||||
\[\@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\)
|
||||
# Twitter hashtag
|
||||
\btwitter\.com/hashtag/[\w?_=&]*
|
||||
# Twitter status
|
||||
\btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)
|
||||
# Twitter profile images
|
||||
\btwimg\.com/profile_images/[_\w./]*
|
||||
# Twitter media
|
||||
\btwimg\.com/media/[-_\w./?=]*
|
||||
# Twitter link shortened
|
||||
\bt\.co/\w+
|
||||
|
||||
# facebook
|
||||
\bfburl\.com/[0-9a-z_]+
|
||||
# facebook CDN
|
||||
\bfbcdn\.net/[\w/.,]*
|
||||
# facebook watch
|
||||
\bfb\.watch/[0-9A-Za-z]+
|
||||
|
||||
# dropbox
|
||||
\bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+
|
||||
|
||||
# ipfs protocol
|
||||
ipfs://[0-9a-z]*
|
||||
# ipfs url
|
||||
/ipfs/[0-9a-z]*
|
||||
|
||||
# w3
|
||||
\bw3\.org/[-0-9a-zA-Z/#.]+
|
||||
|
||||
# loom
|
||||
\bloom\.com/embed/[0-9a-f]+
|
||||
|
||||
# regex101
|
||||
\bregex101\.com/r/[^/\s"]+/\d+
|
||||
|
||||
# figma
|
||||
\bfigma\.com/file(?:/[0-9a-zA-Z]+/)+
|
||||
|
||||
# freecodecamp.org
|
||||
\bfreecodecamp\.org/[-\w/.]+
|
||||
|
||||
# image.tmdb.org
|
||||
\bimage\.tmdb\.org/[/\w.]+
|
||||
|
||||
# mermaid
|
||||
\bmermaid\.ink/img/[-\w]+|\bmermaid-js\.github\.io/mermaid-live-editor/#/edit/[-\w]+
|
||||
|
||||
# Wikipedia
|
||||
\ben\.wikipedia\.org/wiki/[-\w%.#]+
|
||||
|
||||
# gitweb
|
||||
[^"\s]+/gitweb/\S+;h=[0-9a-f]+
|
||||
|
||||
# HyperKitty lists
|
||||
/archives/list/[^@/]+\@[^/\s"]*/message/[^/\s"]*/
|
||||
|
||||
# lists
|
||||
/thread\.html/[^"\s]+
|
||||
|
||||
# list-management
|
||||
\blist-manage\.com/subscribe(?:[?&](?:u|id)=[0-9a-f]+)+
|
||||
|
||||
# kubectl.kubernetes.io/last-applied-configuration
|
||||
"kubectl.kubernetes.io/last-applied-configuration": ".*"
|
||||
|
||||
# pgp
|
||||
\bgnupg\.net/pks/lookup[?&=0-9a-zA-Z]*
|
||||
|
||||
# Spotify
|
||||
\bopen\.spotify\.com/embed/playlist/\w+
|
||||
|
||||
# Mastodon
|
||||
\bmastodon\.[-a-z.]*/(?:media/|\@)[?&=0-9a-zA-Z_]*
|
||||
|
||||
# scastie
|
||||
\bscastie\.scala-lang\.org/[^/]+/\w+
|
||||
|
||||
# images.unsplash.com
|
||||
\bimages\.unsplash\.com/(?:(?:flagged|reserve)/|)[-\w./%?=%&.;]+
|
||||
|
||||
# pastebin
|
||||
\bpastebin\.com/[\w/]+
|
||||
|
||||
# heroku
|
||||
\b\w+\.heroku\.com/source/archive/\w+
|
||||
|
||||
# quip
|
||||
\b\w+\.quip\.com/\w+(?:(?:#|/issues/)\w+)?
|
||||
|
||||
# badgen.net
|
||||
\bbadgen\.net/badge/[^")\]'\s]+
|
||||
|
||||
# statuspage.io
|
||||
\w+\.statuspage\.io\b
|
||||
|
||||
# media.giphy.com
|
||||
\bmedia\.giphy\.com/media/[^/]+/[\w.?&=]+
|
||||
|
||||
# tinyurl
|
||||
\btinyurl\.com/\w+
|
||||
|
||||
# getopts
|
||||
\bgetopts\s+(?:"[^"]+"|'[^']+')
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
|
||||
|
||||
# URL escaped characters
|
||||
\%[0-9A-F][A-F]
|
||||
# IPv6
|
||||
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
|
||||
# c99 hex digits (not the full format, just one I've seen)
|
||||
0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP]
|
||||
# Punycode
|
||||
\bxn--[-0-9a-z]+
|
||||
# sha
|
||||
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
|
||||
# sha-... -- uses a fancy capture
|
||||
(['"]|")[0-9a-f]{40,}\g{-1}
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
# hex in url queries
|
||||
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
|
||||
# ssh
|
||||
(?:ssh-\S+|-nistp256) [-a-zA-Z=;:/0-9+]{12,}
|
||||
|
||||
# PGP
|
||||
\b(?:[0-9A-F]{4} ){9}[0-9A-F]{4}\b
|
||||
# GPG keys
|
||||
\b(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}\b
|
||||
# Well known gpg keys
|
||||
.well-known/openpgpkey/[\w./]+
|
||||
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
# integrity
|
||||
integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}"
|
||||
|
||||
# https://www.gnu.org/software/groff/manual/groff.html
|
||||
# man troff content
|
||||
\\f[BCIPR]
|
||||
# '
|
||||
\\\(aq
|
||||
|
||||
# .desktop mime types
|
||||
^MimeTypes?=.*$
|
||||
# .desktop localized entries
|
||||
^[A-Z][a-z]+\[[a-z]+\]=.*$
|
||||
# Localized .desktop content
|
||||
Name\[[^\]]+\]=.*
|
||||
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# crypt
|
||||
"\$2[ayb]\$.{56}"
|
||||
|
||||
# scrypt / argon
|
||||
\$(?:scrypt|argon\d+[di]*)\$\S+
|
||||
|
||||
# Input to GitHub JSON
|
||||
content: "[-a-zA-Z=;:/0-9+]*="
|
||||
|
||||
# Python stringprefix / binaryprefix
|
||||
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
|
||||
(?<!')\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
|
||||
|
||||
# Regular expressions for (P|p)assword
|
||||
\([A-Z]\|[a-z]\)[a-z]+
|
||||
|
||||
# JavaScript regular expressions
|
||||
# javascript test regex
|
||||
/.*/[gim]*\.test\(
|
||||
# javascript match regex
|
||||
\.match\(/[^/\s"]*/[gim]*\s*
|
||||
# javascript match regex
|
||||
\.match\(/\\[b].*?/[gim]*\s*\)(?:;|$)
|
||||
# javascript regex
|
||||
^\s*/\\[b].*/[gim]*\s*(?:\)(?:;|$)|,$)
|
||||
# javascript replace regex
|
||||
\.replace\(/[^/\s"]*/[gim]*\s*,
|
||||
|
||||
# Go regular expressions
|
||||
regexp?\.MustCompile\(`[^`]*`\)
|
||||
|
||||
# sed regular expressions
|
||||
sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2}
|
||||
|
||||
# go install
|
||||
go install(?:\s+[a-z]+\.[-@\w/.]+)+
|
||||
|
||||
# kubernetes pod status lists
|
||||
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
|
||||
|
||||
# kubectl - pods in CrashLoopBackOff
|
||||
\w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+
|
||||
|
||||
# kubernetes object suffix
|
||||
-[0-9a-f]{10}-\w{5}\s
|
||||
|
||||
# posthog secrets
|
||||
posthog\.init\((['"])phc_[^"',]+\g{-1},
|
||||
|
||||
# xcode
|
||||
|
||||
# xcodeproject scenes
|
||||
(?:Controller|ID|id)="\w{3}-\w{2}-\w{3}"
|
||||
|
||||
# xcode api botches
|
||||
customObjectInstantitationMethod
|
||||
|
||||
# font awesome classes
|
||||
\.fa-[-a-z0-9]+
|
||||
|
||||
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
|
||||
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
|
||||
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
|
||||
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
|
||||
## You could manually change `(?i)X...` to use `[Xx]...`
|
||||
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
|
||||
# Lorem
|
||||
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
|
||||
|
||||
# Non-English
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
|
||||
|
||||
# French
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# latex
|
||||
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
|
||||
|
||||
# the negative lookahead here is to allow catching 'templatesz' as a misspelling
|
||||
# but to otherwise recognize a Windows path with \templates\foo.template or similar:
|
||||
\\(?:necessary|r(?:eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z])
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
# Note that the next example is no longer necessary if you are using
|
||||
# to match a string starting with a `#`, use a character-class:
|
||||
[#]backwards
|
||||
# version suffix <word>v#
|
||||
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
|
||||
# Compiler flags (Scala)
|
||||
(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
# Compiler flags
|
||||
#(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# Compiler flags (linker)
|
||||
,-B
|
||||
# curl arguments
|
||||
\b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
|
||||
# set arguments
|
||||
\bset(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
|
||||
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
|
||||
# macOS temp folders
|
||||
/var/folders/\w\w/[+\w]+/(?:T|-Caches-)/
|
||||
48
.github/actions/spelling/excludes.txt
vendored
48
.github/actions/spelling/excludes.txt
vendored
@@ -1,28 +1,39 @@
|
||||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes
|
||||
(?:(?i)\.png$)
|
||||
(?:^|/)(?i)COPYRIGHT
|
||||
(?:^|/)(?i)LICEN[CS]E
|
||||
(?:^|/)3rdparty/
|
||||
(?:^|/)dirs$
|
||||
(?:^|/)go\.mod$
|
||||
(?:^|/)go\.sum$
|
||||
(?:^|/)package-lock\.json$
|
||||
(?:^|/)package(?:-lock|)\.json$
|
||||
(?:^|/)sources(?:|\.dep)$
|
||||
SUMS$
|
||||
(?:^|/)vendor/
|
||||
\.a$
|
||||
\.ai$
|
||||
\.avi$
|
||||
\.bmp$
|
||||
\.bz2$
|
||||
\.cer$
|
||||
\.class$
|
||||
\.crl$
|
||||
\.crt$
|
||||
\.csr$
|
||||
\.dll$
|
||||
\.docx?$
|
||||
\.drawio$
|
||||
\.DS_Store$
|
||||
\.eot$
|
||||
\.eps$
|
||||
\.exe$
|
||||
\.gif$
|
||||
\.gitattributes$
|
||||
\.graffle$
|
||||
\.gz$
|
||||
\.icns$
|
||||
\.ico$
|
||||
\.jar$
|
||||
\.jks$
|
||||
\.jpeg$
|
||||
\.jpg$
|
||||
\.key$
|
||||
@@ -30,28 +41,53 @@ SUMS$
|
||||
\.lock$
|
||||
\.map$
|
||||
\.min\..
|
||||
\.mod$
|
||||
\.mp3$
|
||||
\.mp4$
|
||||
\.o$
|
||||
\.ocf$
|
||||
\.otf$
|
||||
\.pbxproj$
|
||||
\.pdf$
|
||||
\.pem$
|
||||
\.png$
|
||||
\.psd$
|
||||
\.pyc$
|
||||
\.runsettings$
|
||||
\.s$
|
||||
\.sig$
|
||||
\.so$
|
||||
\.svg$
|
||||
\.svgz$
|
||||
\.svgz?$
|
||||
\.tar$
|
||||
\.tgz$
|
||||
\.tiff?$
|
||||
\.ttf$
|
||||
\.vsdx$
|
||||
\.wav$
|
||||
\.webm$
|
||||
\.webp$
|
||||
\.woff
|
||||
\.woff2?$
|
||||
\.xcf$
|
||||
\.xls
|
||||
\.xlsx?$
|
||||
\.xpm$
|
||||
\.yml$
|
||||
\.zip$
|
||||
^\.github/actions/spelling/
|
||||
^\.github/fabricbot.json$
|
||||
^\.gitignore$
|
||||
^\Q.git-blame-ignore-revs\E$
|
||||
^\Q.github/workflows/spelling.yml\E$
|
||||
^\Qdoc/reference/windows-terminal-logo.ans\E$
|
||||
^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$
|
||||
^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$
|
||||
^\Qsrc/host/ft_host/chafa.txt\E$
|
||||
^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$
|
||||
^\XamlStyler.json$
|
||||
^build/config/
|
||||
^consolegit2gitfilters\.json$
|
||||
^dep/
|
||||
^doc/reference/master-sequence-list.csv$
|
||||
@@ -61,12 +97,14 @@ SUMS$
|
||||
^src/host/runft\.bat$
|
||||
^src/host/runut\.bat$
|
||||
^src/interactivity/onecore/BgfxEngine\.
|
||||
^src/renderer/atlas/
|
||||
^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/terminal/parser/ft_fuzzwrapper/run\.bat$
|
||||
^src/terminal/parser/ut_parser/Base64Test.cpp$
|
||||
^src/terminal/parser/ut_parser/run\.bat$
|
||||
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
|
||||
^src/tools/lnkd/lnkd\.bat$
|
||||
@@ -74,6 +112,6 @@ SUMS$
|
||||
^src/tools/texttests/fira\.txt$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^src/types/ut_types/UtilsTests.cpp$
|
||||
^\.github/actions/spelling/
|
||||
^\.gitignore$
|
||||
^\XamlStyler.json$
|
||||
^tools/ReleaseEngineering/ServicingPipeline.ps1$
|
||||
ignore$
|
||||
SUMS$
|
||||
|
||||
8
.github/actions/spelling/expect/alphabet.txt
vendored
8
.github/actions/spelling/expect/alphabet.txt
vendored
@@ -5,26 +5,19 @@ AAAAAABBBBBBCCC
|
||||
AAAAABBBBBBCCC
|
||||
abcd
|
||||
abcd
|
||||
abcde
|
||||
abcdef
|
||||
ABCDEFG
|
||||
ABCDEFGH
|
||||
ABCDEFGHIJ
|
||||
abcdefghijk
|
||||
ABCDEFGHIJKLMNO
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
ABCG
|
||||
ABE
|
||||
abf
|
||||
BBBBB
|
||||
BBBBBBBB
|
||||
BBBBBBBBBBBBBBDDDD
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBGGRR
|
||||
CCE
|
||||
EFG
|
||||
EFGh
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
@@ -33,7 +26,6 @@ QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
|
||||
qrstuvwxyz
|
||||
qwerty
|
||||
QWERTYUIOP
|
||||
qwertyuiopasdfg
|
||||
YYYYYYYDDDDDDDDDDD
|
||||
ZAAZZ
|
||||
|
||||
947
.github/actions/spelling/expect/expect.txt
vendored
947
.github/actions/spelling/expect/expect.txt
vendored
File diff suppressed because it is too large
Load Diff
12
.github/actions/spelling/expect/web.txt
vendored
12
.github/actions/spelling/expect/web.txt
vendored
@@ -1,16 +1,6 @@
|
||||
http
|
||||
www
|
||||
ecma
|
||||
rapidtables
|
||||
WCAG
|
||||
freedesktop
|
||||
ycombinator
|
||||
robertelder
|
||||
kovidgoyal
|
||||
leonerd
|
||||
fixterms
|
||||
winui
|
||||
appshellintegration
|
||||
cppreference
|
||||
mdtauk
|
||||
gfycat
|
||||
Guake
|
||||
|
||||
62
.github/actions/spelling/line_forbidden.patterns
vendored
Normal file
62
.github/actions/spelling/line_forbidden.patterns
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
# reject `m_data` as there's a certain OS which has evil defines that break things if it's used elsewhere
|
||||
# \bm_data\b
|
||||
|
||||
# If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test,
|
||||
# you might not want to check in code where you were debugging w/ `fit()`, in which case, you might want
|
||||
# to use this:
|
||||
#\bfit\(
|
||||
|
||||
# s.b. GitHub
|
||||
\bGithub\b
|
||||
|
||||
# s.b. GitLab
|
||||
\bGitlab\b
|
||||
|
||||
# s.b. JavaScript
|
||||
\bJavascript\b
|
||||
|
||||
# s.b. Microsoft
|
||||
\bMicroSoft\b
|
||||
|
||||
# s.b. another
|
||||
\ban[- ]other\b
|
||||
|
||||
# s.b. greater than
|
||||
\bgreater then\b
|
||||
|
||||
# s.b. into
|
||||
#\sin to\s
|
||||
|
||||
# s.b. opt-in
|
||||
\sopt in\s
|
||||
|
||||
# s.b. less than
|
||||
\bless then\b
|
||||
|
||||
# s.b. otherwise
|
||||
\bother[- ]wise\b
|
||||
|
||||
# s.b. nonexistent
|
||||
\bnon existing\b
|
||||
\b[Nn]o[nt][- ]existent\b
|
||||
|
||||
# s.b. preexisting
|
||||
[Pp]re[- ]existing
|
||||
|
||||
# s.b. preempt
|
||||
[Pp]re[- ]empt\b
|
||||
|
||||
# s.b. preemptively
|
||||
[Pp]re[- ]emptively
|
||||
|
||||
# s.b. reentrancy
|
||||
[Rr]e[- ]entrancy
|
||||
|
||||
# s.b. reentrant
|
||||
[Rr]e[- ]entrant
|
||||
|
||||
# s.b. workaround(s)
|
||||
#\bwork[- ]arounds?\b
|
||||
|
||||
# Reject duplicate words
|
||||
\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s
|
||||
86
.github/actions/spelling/patterns/patterns.txt
vendored
86
.github/actions/spelling/patterns/patterns.txt
vendored
@@ -1,11 +1,6 @@
|
||||
https://(?:(?:[-a-zA-Z0-9?&=]*\.|)microsoft\.com)/[-a-zA-Z0-9?&=_#\/.]*
|
||||
https://aka\.ms/[-a-zA-Z0-9?&=\/_]*
|
||||
https://www\.itscj\.ipsj\.or\.jp/iso-ir/[-0-9]+\.pdf
|
||||
https://www\.vt100\.net/docs/[-a-zA-Z0-9#_\/.]*
|
||||
https://www.w3.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
https://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]*
|
||||
https://(?:[a-z-]+\.|)github(?:usercontent|)\.com/[-a-zA-Z0-9?%&=_\/.]*
|
||||
https://www.xfree86.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
|
||||
|
||||
https?://\S+
|
||||
[Pp]ublicKeyToken="?[0-9a-fA-F]{16}"?
|
||||
(?:[{"]|UniqueIdentifier>)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|</UniqueIdentifier)
|
||||
(?:0[Xx]|\\x|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]{0,2}\b
|
||||
@@ -24,3 +19,78 @@ VERIFY_ARE_EQUAL\(L"[^"]+"
|
||||
std::memory_order_[\w]+
|
||||
D2DERR_SHADER_COMPILE_FAILED
|
||||
TIL_FEATURE_[0-9A-Z_]+
|
||||
vcvars\w*
|
||||
ROY\sG\.\sBIV
|
||||
!(?:(?i)ESC)!\[
|
||||
!(?:(?i)CSI)!(?:\d+(?:;\d+|)m|[ABCDF])
|
||||
|
||||
# Python stringprefix / binaryprefix
|
||||
\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'
|
||||
|
||||
# Automatically suggested patterns
|
||||
# hit-count: 3831 file-count: 582
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# hit-count: 71 file-count: 35
|
||||
# Compiler flags
|
||||
(?:^|[\t ,"'`=(])-[D](?=[A-Z]{2,}|[A-Z][a-z])
|
||||
(?:^|[\t ,"'`=(])-[X](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# hit-count: 41 file-count: 28
|
||||
# version suffix <word>v#
|
||||
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
|
||||
|
||||
# hit-count: 20 file-count: 9
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
|
||||
# hit-count: 10 file-count: 7
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
|
||||
# hit-count: 4 file-count: 4
|
||||
# mailto urls
|
||||
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
|
||||
|
||||
# hit-count: 4 file-count: 1
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
|
||||
|
||||
# hit-count: 2 file-count: 1
|
||||
# latex
|
||||
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# Non-English
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# French
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# acceptable duplicates
|
||||
# ls directory listings
|
||||
[-bcdlpsw](?:[-r][-w][-sx]){3}\s+\d+\s+(\S+)\s+\g{-1}\s+\d+\s+
|
||||
# C/idl types + English ...
|
||||
\s(Guid|long|LONG|that) \g{-1}\s
|
||||
|
||||
# javadoc / .net
|
||||
(?:[\\@](?:groupname|param)|(?:public|private)(?:\s+static|\s+readonly)*)\s+(\w+)\s+\g{-1}\s
|
||||
|
||||
# Commit message -- Signed-off-by and friends
|
||||
^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$
|
||||
|
||||
# Autogenerated revert commit message
|
||||
^This reverts commit [0-9a-f]{40}\.$
|
||||
|
||||
# vtmode
|
||||
--vtmode\s+(\w+)\s+\g{-1}\s
|
||||
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
|
||||
28
.github/actions/spelling/reject.txt
vendored
28
.github/actions/spelling/reject.txt
vendored
@@ -1,22 +1,12 @@
|
||||
^attache$
|
||||
^attacher$
|
||||
^attachers$
|
||||
^spae$
|
||||
^spaebook$
|
||||
^spaecraft$
|
||||
^spaed$
|
||||
^spaedom$
|
||||
^spaeing$
|
||||
^spaeings$
|
||||
^spae-man$
|
||||
^spaeman$
|
||||
^spaer$
|
||||
^Spaerobee$
|
||||
^spaes$
|
||||
^spaewife$
|
||||
^spaewoman$
|
||||
^spaework$
|
||||
^spaewright$
|
||||
^wether$
|
||||
^wethers$
|
||||
^wetherteg$
|
||||
benefitting
|
||||
occurences?
|
||||
^dependan.*
|
||||
^oer$
|
||||
Sorce
|
||||
^[Ss]pae.*
|
||||
^untill$
|
||||
^untilling$
|
||||
^wether.*
|
||||
|
||||
19
.github/workflows/spelling.yml
vendored
19
.github/workflows/spelling.yml
vendored
@@ -1,19 +0,0 @@
|
||||
name: Spell checking
|
||||
on:
|
||||
pull_request_target:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Spell checking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout-merge
|
||||
if: "contains(github.event_name, 'pull_request')"
|
||||
uses: actions/checkout@v2.0.0
|
||||
with:
|
||||
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
|
||||
134
.github/workflows/spelling2.yml
vendored
Normal file
134
.github/workflows/spelling2.yml
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
# spelling.yml is blocked per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-g86g-chm8-7r2p
|
||||
name: Spell checking
|
||||
|
||||
# Comment management is handled through a secondary job, for details see:
|
||||
# https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions
|
||||
#
|
||||
# `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# (in odd cases, it might actually run just to collapse a commment, but that's fairly rare)
|
||||
# it needs `contents: write` in order to add a comment.
|
||||
#
|
||||
# `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# or collapse a comment (in the case where it had previously made a comment and now no longer needs to show a comment)
|
||||
# it needs `pull-requests: write` in order to manipulate those comments.
|
||||
|
||||
# Updating pull request branches is managed via comment handling.
|
||||
# For details, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-expect-list
|
||||
#
|
||||
# These elements work together to make it happen:
|
||||
#
|
||||
# `on.issue_comment`
|
||||
# This event listens to comments by users asking to update the metadata.
|
||||
#
|
||||
# `jobs.update`
|
||||
# This job runs in response to an issue_comment and will push a new commit
|
||||
# to update the spelling metadata.
|
||||
#
|
||||
# `with.experimental_apply_changes_via_bot`
|
||||
# Tells the action to support and generate messages that enable it
|
||||
# to make a commit to update the spelling metadata.
|
||||
#
|
||||
# `with.ssh_key`
|
||||
# In order to trigger workflows when the commit is made, you can provide a
|
||||
# secret (typically, a write-enabled github deploy key).
|
||||
#
|
||||
# For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**"
|
||||
types:
|
||||
- 'opened'
|
||||
- 'reopened'
|
||||
- 'synchronize'
|
||||
issue_comment:
|
||||
types:
|
||||
- 'created'
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
name: Spell checking
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
actions: read
|
||||
outputs:
|
||||
followup: ${{ steps.spelling.outputs.followup }}
|
||||
runs-on: ubuntu-latest
|
||||
if: "contains(github.event_name, 'pull_request') || github.event_name == 'push'"
|
||||
concurrency:
|
||||
group: spelling-${{ github.event.pull_request.number || github.ref }}
|
||||
# note: If you use only_check_changed_files, you do not want cancel-in-progress
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
suppress_push_for_open_pull_request: 1
|
||||
checkout: true
|
||||
check_file_names: 1
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
post_comment: 0
|
||||
use_magic_file: 1
|
||||
extra_dictionary_limit: 10
|
||||
extra_dictionaries:
|
||||
cspell:software-terms/src/software-terms.txt
|
||||
cspell:python/src/python/python-lib.txt
|
||||
cspell:node/node.txt
|
||||
cspell:cpp/src/stdlib-c.txt
|
||||
cspell:cpp/src/stdlib-cpp.txt
|
||||
cspell:fullstack/fullstack.txt
|
||||
cspell:filetypes/filetypes.txt
|
||||
cspell:html/html.txt
|
||||
cspell:cpp/src/compiler-msvc.txt
|
||||
cspell:python/src/common/extra.txt
|
||||
cspell:powershell/powershell.txt
|
||||
cspell:aws/aws.txt
|
||||
cspell:cpp/src/lang-keywords.txt
|
||||
cspell:npm/npm.txt
|
||||
cspell:dotnet/dotnet.txt
|
||||
cspell:python/src/python/python.txt
|
||||
cspell:css/css.txt
|
||||
cspell:cpp/src/stdlib-cmath.txt
|
||||
check_extra_dictionaries: ''
|
||||
|
||||
comment-push:
|
||||
name: Report (Push)
|
||||
# If your workflow isn't running on push, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
needs: spelling
|
||||
permissions:
|
||||
contents: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
|
||||
comment-pr:
|
||||
name: Report (PR)
|
||||
# If you workflow isn't running on pull_request*, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
needs: spelling
|
||||
permissions:
|
||||
pull-requests: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
@@ -73,6 +73,7 @@ EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.unittest", "src\host\ut_lib\host.unittest.vcxproj", "{06EC74CB-9A12-429C-B551-8562EC954747}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263}
|
||||
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Tests.Unit", "src\host\ut_host\Host.UnitTests.vcxproj", "{531C23E7-4B76-4C08-8AAD-04164CB628C9}"
|
||||
|
||||
@@ -21,7 +21,7 @@ Write-Host "Checking test results..."
|
||||
$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
|
||||
[System.Collections.Generic.List[string]]$failingTests = @()
|
||||
[System.Collections.Generic.List[string]]$unreliableTests = @()
|
||||
[System.Collections.Generic.List[string]]$unexpectedResultTest = @()
|
||||
@@ -50,7 +50,7 @@ foreach ($testRun in ($testRuns.value | Sort-Object -Property "completedDate" -D
|
||||
$totalTestsExecutedCount += $testRun.totalTests
|
||||
|
||||
$testRunResultsUri = "$($testRun.url)/results?api-version=5.0"
|
||||
$testResults = Invoke-RestMethod -Uri "$($testRun.url)/results?api-version=5.0" -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
$testResults = Invoke-RestMethodWithRetries "$($testRun.url)/results?api-version=5.0" -Headers $azureDevOpsRestApiHeaders
|
||||
|
||||
foreach ($testResult in $testResults.value)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ $payloadDir = "HelixPayload\$Configuration\$Platform"
|
||||
|
||||
$repoDirectory = Join-Path (Split-Path -Parent $script:MyInvocation.MyCommand.Path) "..\..\"
|
||||
$nugetPackagesDir = Join-Path (Split-Path -Parent $script:MyInvocation.MyCommand.Path) "packages"
|
||||
|
||||
|
||||
# Create the payload directory. Remove it if it already exists.
|
||||
If(test-path $payloadDir)
|
||||
{
|
||||
@@ -19,8 +19,8 @@ New-Item -ItemType Directory -Force -Path $payloadDir
|
||||
|
||||
# Copy files from nuget packages
|
||||
Copy-Item "$nugetPackagesDir\microsoft.windows.apps.test.1.0.181203002\lib\netcoreapp2.1\*.dll" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\*" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\NetFx4.5\*" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.60.210621002\build\Binaries\$Platform\*" $payloadDir
|
||||
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.60.210621002\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\"
|
||||
@@ -59,7 +59,7 @@ Copy-Item "build\Helix\EnsureMachineState.ps1" "$payloadDir"
|
||||
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
|
||||
# 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
|
||||
|
||||
@@ -20,13 +20,31 @@ function Generate-File-Links
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<ul>"
|
||||
foreach($file in $files)
|
||||
{
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<li><a href=$($file.Link)>$($file.Name)</a></li>"
|
||||
$url = Append-HelixAccessTokenToUrl $file.Link "{Your-Helix-Access-Token-Here}"
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<li>$($url)</li>"
|
||||
}
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "</ul>"
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "</div>"
|
||||
}
|
||||
}
|
||||
|
||||
function Append-HelixAccessTokenToUrl
|
||||
{
|
||||
Param ([string]$url, [string]$token)
|
||||
if($token)
|
||||
{
|
||||
if($url.Contains("?"))
|
||||
{
|
||||
$url = "$($url)&access_token=$($token)"
|
||||
}
|
||||
else
|
||||
{
|
||||
$url = "$($url)?access_token=$($token)"
|
||||
}
|
||||
}
|
||||
return $url
|
||||
}
|
||||
|
||||
#Create output directory
|
||||
New-Item $OutputFolder -ItemType Directory
|
||||
|
||||
@@ -63,7 +81,8 @@ foreach ($testRun in $testRuns.value)
|
||||
if (-not $workItems.Contains($workItem))
|
||||
{
|
||||
$workItems.Add($workItem)
|
||||
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files$accessTokenParam"
|
||||
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files"
|
||||
$filesQueryUri = Append-HelixAccessTokenToUrl $filesQueryUri $helixAccessToken
|
||||
$files = Invoke-RestMethodWithRetries $filesQueryUri
|
||||
|
||||
$screenShots = $files | where { $_.Name.EndsWith(".jpg") }
|
||||
@@ -102,6 +121,7 @@ foreach ($testRun in $testRuns.value)
|
||||
|
||||
Write-Host "Downloading $link to $destination"
|
||||
|
||||
$link = Append-HelixAccessTokenToUrl $link $HelixAccessToken
|
||||
Download-FileWithRetries $link $destination
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ Write-Host "queryUri = $queryUri"
|
||||
# To account for unreliable tests, we'll iterate through all of the tests associated with this build, check to see any tests that were unreliable
|
||||
# (denoted by being marked as "skipped"), and if so, we'll instead mark those tests with a warning and enumerate all of the attempted runs
|
||||
# with their pass/fail states as well as any relevant error messages for failed attempts.
|
||||
$testRuns = Invoke-RestMethod -Uri $queryUri -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
|
||||
|
||||
$timesSeenByRunName = @{}
|
||||
|
||||
@@ -32,10 +32,10 @@ foreach ($testRun in $testRuns.value)
|
||||
$testRunResultsUri = "$($testRun.url)/results?api-version=5.0"
|
||||
|
||||
Write-Host "Marking test run `"$($testRun.name)`" as in progress so we can change its results to account for unreliable tests."
|
||||
Invoke-RestMethod -Uri "$($testRun.url)?api-version=5.0" -Method Patch -Body (ConvertTo-Json @{ "state" = "InProgress" }) -Headers $azureDevOpsRestApiHeaders -ContentType "application/json" | Out-Null
|
||||
Invoke-RestMethod "$($testRun.url)?api-version=5.0" -Method Patch -Body (ConvertTo-Json @{ "state" = "InProgress" }) -Headers $azureDevOpsRestApiHeaders -ContentType "application/json" | Out-Null
|
||||
|
||||
Write-Host "Retrieving test results..."
|
||||
$testResults = Invoke-RestMethod -Uri $testRunResultsUri -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
$testResults = Invoke-RestMethodWithRetries $testRunResultsUri -Headers $azureDevOpsRestApiHeaders
|
||||
|
||||
foreach ($testResult in $testResults.value)
|
||||
{
|
||||
@@ -54,7 +54,8 @@ foreach ($testRun in $testRuns.value)
|
||||
Write-Host " Test $($testResult.testCaseTitle) was detected as unreliable. Updating..."
|
||||
|
||||
# The errorMessage field contains a link to the JSON-encoded rerun result data.
|
||||
$rerunResults = ConvertFrom-Json (New-Object System.Net.WebClient).DownloadString($testResult.errorMessage)
|
||||
$resultsJson = Download-StringWithRetries "Error results" $testResult.errorMessage
|
||||
$rerunResults = ConvertFrom-Json $resultsJson
|
||||
[System.Collections.Generic.List[System.Collections.Hashtable]]$rerunDataList = @()
|
||||
$attemptCount = 0
|
||||
$passCount = 0
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<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.Taef" version="10.60.210621002" targetFramework="native" />
|
||||
<package id="microsoft.windows.apps.test" version="1.0.181203002" targetFramework="native" />
|
||||
<package id="runtime.win-x86.microsoft.netcore.app" version="2.1.0" targetFramework="native" />
|
||||
<package id="runtime.win-x64.microsoft.netcore.app" version="2.1.0" targetFramework="native" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.58.210305002" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -1,48 +1,487 @@
|
||||
# This build should never run as CI or against a pull request.
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
pool:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
|
||||
parameters:
|
||||
- name: branding
|
||||
displayName: "Branding (Build Type)"
|
||||
type: string
|
||||
default: Release
|
||||
values:
|
||||
- Release
|
||||
- Preview
|
||||
- name: buildTerminal
|
||||
displayName: "Build Windows Terminal MSIX"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: buildTerminalVPack
|
||||
displayName: "Build Windows Terminal VPack"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: buildWPF
|
||||
displayName: "Build Terminal WPF Control"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: pgoBuildMode
|
||||
displayName: "PGO Build Mode"
|
||||
type: string
|
||||
default: Optimize
|
||||
values:
|
||||
- Optimize
|
||||
- Instrument
|
||||
- None
|
||||
|
||||
- name: buildConfigurations
|
||||
type: object
|
||||
default:
|
||||
- Release
|
||||
- name: buildPlatforms
|
||||
type: object
|
||||
default:
|
||||
- x64
|
||||
- x86
|
||||
- arm64
|
||||
|
||||
variables:
|
||||
baseYearForVersioning: 2019 # Used by build-console-int
|
||||
versionMajor: 0
|
||||
versionMinor: 1
|
||||
TerminalInternalPackageVersion: "0.0.7"
|
||||
|
||||
# When we move off PackageES for Versioning, we'll need to switch
|
||||
# name to this format. For now, though, we need to use DayOfYear.Rev
|
||||
# to unique our builds, as mandated by PackageES's Setup task.
|
||||
# name: '$(versionMajor).$(versionMinor).$(DayOfYear)$(Rev:r).0'
|
||||
#
|
||||
# Build name/version number above must end with .0 to make the
|
||||
# store publication machinery happy.
|
||||
name: 'Terminal_$(date:yyMM).$(date:dd)$(rev:rrr)'
|
||||
|
||||
# Build Arguments:
|
||||
# WindowsTerminalOfficialBuild=[true,false]
|
||||
# true - this is running on our build agent
|
||||
# false - running locally
|
||||
# WindowsTerminalBranding=[Dev,Preview,Release]
|
||||
# <none> - Development build resources (default)
|
||||
# Preview - Preview build resources
|
||||
# Release - regular build resources
|
||||
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
|
||||
resources:
|
||||
repositories:
|
||||
- repository: self
|
||||
type: git
|
||||
ref: main
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
- job: Build
|
||||
strategy:
|
||||
matrix:
|
||||
${{ each config in parameters.buildConfigurations }}:
|
||||
${{ each platform in parameters.buildPlatforms }}:
|
||||
${{ config }}_${{ platform }}:
|
||||
BuildConfiguration: ${{ config }}
|
||||
BuildPlatform: ${{ platform }}
|
||||
displayName: Build
|
||||
cancelTimeoutInMinutes: 1
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- task: PowerShell@2
|
||||
displayName: Rationalize Build Platform
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Arch = "$(BuildPlatform)"
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
If ($Arch -Eq "x86") { $Arch = "Win32" }
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
- task: NuGetToolInstaller@1
|
||||
displayName: Use NuGet 5.10
|
||||
inputs:
|
||||
versionSpec: 5.10
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet custom
|
||||
inputs:
|
||||
command: custom
|
||||
selectOrConfig: config
|
||||
nugetConfigPath: NuGet.Config
|
||||
arguments: restore OpenConsole.sln -SolutionDirectory $(Build.SourcesDirectory)
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
inputs:
|
||||
feedListDownload: 2b3f8893-a6e8-411f-b197-a9e05576da48
|
||||
packageListDownload: e82d490c-af86-4733-9dc4-07b772033204
|
||||
versionListDownload: $(TerminalInternalPackageVersion)
|
||||
- task: TouchdownBuildTask@1
|
||||
displayName: Download Localization Files
|
||||
inputs:
|
||||
teamId: 7105
|
||||
authId: $(TouchdownAppId)
|
||||
authKey: $(TouchdownAppKey)
|
||||
resourceFilePath: >-
|
||||
src\cascadia\TerminalApp\Resources\en-US\Resources.resw
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: arm64
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
src\cascadia\TerminalControl\Resources\en-US\Resources.resw
|
||||
|
||||
- template: ./templates/check-formatting.yml
|
||||
src\cascadia\TerminalConnection\Resources\en-US\Resources.resw
|
||||
|
||||
- template: ./templates/release-sign-and-bundle.yml
|
||||
src\cascadia\TerminalSettingsModel\Resources\en-US\Resources.resw
|
||||
|
||||
src\cascadia\TerminalSettingsEditor\Resources\en-US\Resources.resw
|
||||
|
||||
src\cascadia\WindowsTerminalUniversal\Resources\en-US\Resources.resw
|
||||
|
||||
src\cascadia\CascadiaPackage\Resources\en-US\Resources.resw
|
||||
appendRelativeDir: true
|
||||
localizationTarget: false
|
||||
pseudoSetting: Included
|
||||
- task: PowerShell@2
|
||||
displayName: Move Loc files one level up
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Files = Get-ChildItem . -R -Filter 'Resources.resw' | ? FullName -Like '*en-US\*\Resources.resw'
|
||||
|
||||
$Files | % { Move-Item -Verbose $_.Directory $_.Directory.Parent.Parent -EA:Ignore }
|
||||
pwsh: true
|
||||
- task: PowerShell@2
|
||||
displayName: Generate NOTICE.html from NOTICE.md
|
||||
inputs:
|
||||
filePath: .\build\scripts\Generate-ThirdPartyNotices.ps1
|
||||
arguments: -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
|
||||
pwsh: true
|
||||
- ${{ if eq(parameters.pgoBuildMode, 'Optimize') }}:
|
||||
- task: PowerShell@2
|
||||
displayName: Restore PGO Database
|
||||
inputs:
|
||||
filePath: tools/PGODatabase/restore-pgodb.ps1
|
||||
workingDirectory: $(Build.SourcesDirectory)\tools\PGODatabase
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /t:Terminal\CascadiaPackage;Terminal\WindowsTerminalUniversal /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: true
|
||||
maximumCpuCount: true
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact: binlog'
|
||||
condition: failed()
|
||||
continueOnError: True
|
||||
inputs:
|
||||
PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog
|
||||
ArtifactName: binlog-$(BuildPlatform)
|
||||
- ${{ if eq(parameters.pgoBuildMode, 'Optimize') }}:
|
||||
- task: PowerShell@2
|
||||
displayName: Validate binaries are optimized
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
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)/src/cascadia/CascadiaPackage/bin/$(BuildPlatform)/$(BuildConfiguration)/$BinFile"
|
||||
|
||||
}
|
||||
- task: PowerShell@2
|
||||
displayName: Check MSIX for common regressions
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Package = Get-ChildItem -Recurse -Filter "CascadiaPackage_*.msix"
|
||||
|
||||
.\build\scripts\Test-WindowsTerminalPackage.ps1 -Verbose -Path $Package.FullName
|
||||
pwsh: true
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln for PublicTerminalCore
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
- task: PowerShell@2
|
||||
displayName: Source Index PDBs
|
||||
inputs:
|
||||
filePath: build\scripts\Index-Pdbs.ps1
|
||||
arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
|
||||
errorActionPreference: silentlyContinue
|
||||
- task: ComponentGovernanceComponentDetection@0
|
||||
displayName: Component Detection
|
||||
- task: PowerShell@2
|
||||
displayName: Run Unit Tests
|
||||
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
|
||||
enabled: False
|
||||
inputs:
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*unit.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
|
||||
- task: PowerShell@2
|
||||
displayName: Run Feature Tests
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
enabled: False
|
||||
inputs:
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy *.appx/*.msix to Artifacts
|
||||
inputs:
|
||||
Contents: >-
|
||||
**/*.appx
|
||||
|
||||
**/*.msix
|
||||
|
||||
**/*.appxsym
|
||||
|
||||
!**/Microsoft.VCLibs*.appx
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/appx
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (appx)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/appx
|
||||
ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy PublicTerminalCore.dll to Artifacts
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
inputs:
|
||||
Contents: >-
|
||||
**/PublicTerminalCore.dll
|
||||
|
||||
**/api-ms-win-core-synch-l1-2-0.dll
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/wpf
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (PublicTerminalCore)
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/wpf
|
||||
ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)
|
||||
- task: PublishSymbols@2
|
||||
displayName: Publish symbols path
|
||||
continueOnError: True
|
||||
inputs:
|
||||
SearchPattern: '**/*.pdb'
|
||||
IndexSources: false
|
||||
SymbolServerType: TeamServices
|
||||
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- job: BundleAndSign
|
||||
displayName: Create and sign AppX/MSIX bundles
|
||||
dependsOn: Build
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Artifacts (*.appx, *.msix)
|
||||
inputs:
|
||||
downloadType: specific
|
||||
itemPattern: >-
|
||||
**/*.msix
|
||||
|
||||
**/*.appx
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Create WindowsTerminal*.msixbundle
|
||||
inputs:
|
||||
filePath: build\scripts\Create-AppxBundle.ps1
|
||||
arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion 0.0.0.0 -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
|
||||
- task: PowerShell@2
|
||||
displayName: Create WindowsTerminalUniversal*.msixbundle
|
||||
inputs:
|
||||
filePath: build\scripts\Create-AppxBundle.ps1
|
||||
arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName WindowsTerminalUniversal -BundleVersion $(XES_APPXMANIFESTVERSION) -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminalUniversal_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: Submit *.msixbundle to ESRP for code signing
|
||||
inputs:
|
||||
ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
|
||||
FolderPath: $(System.ArtifactsDirectory)
|
||||
Pattern: Microsoft.WindowsTerminal*.msixbundle
|
||||
UseMinimatch: true
|
||||
signConfigType: inlineSignParams
|
||||
inlineOperation: >-
|
||||
[
|
||||
{
|
||||
"KeyCode": "Dynamic",
|
||||
"CertTemplateName": "WINMSAPP1ST",
|
||||
"CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
|
||||
"OperationCode": "SigntoolSign",
|
||||
"Parameters": {
|
||||
"OpusName": "Microsoft",
|
||||
"OpusInfo": "http://www.microsoft.com",
|
||||
"FileDigest": "/fd \"SHA256\"",
|
||||
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
|
||||
},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"KeyCode": "Dynamic",
|
||||
"CertTemplateName": "WINMSAPP1ST",
|
||||
"CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
|
||||
"OperationCode": "SigntoolVerify",
|
||||
"Parameters": {},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact: appxbundle-signed'
|
||||
inputs:
|
||||
PathtoPublish: $(System.ArtifactsDirectory)
|
||||
ArtifactName: appxbundle-signed
|
||||
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- job: PackageAndSignWPF
|
||||
strategy:
|
||||
matrix:
|
||||
${{ each config in parameters.buildConfigurations }}:
|
||||
${{ config }}:
|
||||
BuildConfiguration: ${{ config }}
|
||||
displayName: Create NuGet Package (WPF Terminal Control)
|
||||
dependsOn: Build
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download x86 PublicTerminalCore
|
||||
inputs:
|
||||
artifactName: wpf-dll-x86-$(BuildConfiguration)
|
||||
itemPattern: '**/*.dll'
|
||||
downloadPath: bin\Win32\$(BuildConfiguration)\
|
||||
extractTars: false
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download x64 PublicTerminalCore
|
||||
inputs:
|
||||
artifactName: wpf-dll-x64-$(BuildConfiguration)
|
||||
itemPattern: '**/*.dll'
|
||||
downloadPath: bin\x64\$(BuildConfiguration)\
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Move downloaded artifacts up a level
|
||||
inputs:
|
||||
targetType: inline
|
||||
# Find all artifact files and move them up a directory. Ugh.
|
||||
script: >-
|
||||
Get-ChildItem bin -Recurse -Directory -Filter wpf-dll-* | % {
|
||||
$_ | Get-ChildItem -Recurse -File | % {
|
||||
Move-Item -Verbose $_.FullName $_.Directory.Parent.FullName
|
||||
}
|
||||
}
|
||||
- task: NuGetToolInstaller@1
|
||||
displayName: Use NuGet 5.10.0
|
||||
inputs:
|
||||
versionSpec: 5.10.0
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet restore copy
|
||||
inputs:
|
||||
selectOrConfig: config
|
||||
nugetConfigPath: NuGet.Config
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln for WPF Control
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalReleaseBuild=$(UseReleaseBranding);Version=$(XES_PACKAGEVERSIONNUMBER) /t:Pack
|
||||
platform: Any CPU
|
||||
configuration: $(BuildConfiguration)
|
||||
maximumCpuCount: true
|
||||
- task: PublishSymbols@2
|
||||
displayName: Publish symbols path
|
||||
continueOnError: True
|
||||
inputs:
|
||||
SearchPattern: '**/*.pdb'
|
||||
IndexSources: false
|
||||
SymbolServerType: TeamServices
|
||||
SymbolsArtifactName: Symbols_WPF_$(BuildConfiguration)
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy *.nupkg to Artifacts
|
||||
inputs:
|
||||
Contents: '**/*Wpf*.nupkg'
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/nupkg
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: Submit *.nupkg to ESRP for code signing
|
||||
inputs:
|
||||
ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
|
||||
FolderPath: $(Build.ArtifactStagingDirectory)/nupkg
|
||||
Pattern: '*.nupkg'
|
||||
UseMinimatch: true
|
||||
signConfigType: inlineSignParams
|
||||
inlineOperation: >-
|
||||
[
|
||||
{
|
||||
"KeyCode": "CP-401405",
|
||||
"OperationCode": "NuGetSign",
|
||||
"Parameters": {},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"KeyCode": "CP-401405",
|
||||
"OperationCode": "NuGetVerify",
|
||||
"Parameters": {},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (nupkg)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)\nupkg
|
||||
ArtifactName: wpf-nupkg-$(BuildConfiguration)
|
||||
|
||||
- ${{ if eq(parameters.buildTerminalVPack, true) }}:
|
||||
- job: VPack
|
||||
displayName: Create Windows vPack
|
||||
dependsOn: BundleAndSign
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Build Artifacts
|
||||
inputs:
|
||||
artifactName: appxbundle-signed
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Rename and stage packages for vpack
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
# Rename to known/fixed name for Windows build system
|
||||
|
||||
Get-ChildItem Microsoft.WindowsTerminal_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' }
|
||||
|
||||
|
||||
# Create vpack directory and place item inside
|
||||
|
||||
mkdir WindowsTerminal.app
|
||||
|
||||
mv Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle .\WindowsTerminal.app\
|
||||
workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed
|
||||
- task: PkgESVPack@12
|
||||
displayName: 'Package ES - VPack'
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
inputs:
|
||||
sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed\WindowsTerminal.app
|
||||
description: Windows Terminal pre-install application
|
||||
pushPkgName: WindowsTerminal.app
|
||||
owner: condev
|
||||
...
|
||||
|
||||
@@ -8,9 +8,12 @@ jobs:
|
||||
variables:
|
||||
BuildConfiguration: AuditMode
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool: "windevbuildagents"
|
||||
# The public pool is also an option!
|
||||
# pool: { vmImage: windows-2019 }
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
|
||||
@@ -11,9 +11,12 @@ jobs:
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool: "windevbuildagents"
|
||||
# The public pool is also an option!
|
||||
# pool: { vmImage: windows-2019 }
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
parameters:
|
||||
configuration: 'Release'
|
||||
platform: ''
|
||||
additionalBuildArguments: ''
|
||||
|
||||
jobs:
|
||||
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }}
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
PGOBuildMode: 'Optimize'
|
||||
|
||||
pool:
|
||||
name: Package ES Lab E
|
||||
demands:
|
||||
- msbuild
|
||||
- visualstudio
|
||||
- vstest
|
||||
|
||||
steps:
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: 'Package ES - Setup Build'
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: WindowsTerminal
|
||||
disableOutputRedirect: true
|
||||
|
||||
- template: build-console-steps.yml
|
||||
parameters:
|
||||
additionalBuildArguments: "/p:XesUseOneStoreVersioning=true;XesBaseYearForStoreVersion=$(baseYearForVersioning) ${{ parameters.additionalBuildArguments }}"
|
||||
@@ -3,7 +3,7 @@ parameters:
|
||||
platform: ''
|
||||
additionalBuildArguments: ''
|
||||
minimumExpectedTestsExecutedCount: 1 # Sanity check for minimum expected tests to be reported
|
||||
rerunPassesRequiredToAvoidFailure: 0
|
||||
rerunPassesRequiredToAvoidFailure: 5
|
||||
|
||||
jobs:
|
||||
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
@@ -12,9 +12,12 @@ jobs:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
PGOBuildMode: 'Instrument'
|
||||
pool: "windevbuildagents"
|
||||
# The public pool is also an option!
|
||||
# pool: { vmImage: windows-2019 }
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
clean: true
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Code Formattting Check'
|
||||
displayName: 'Code Formatting Check'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: '.\build\scripts\Invoke-FormattingCheck.ps1'
|
||||
|
||||
@@ -12,4 +12,4 @@ steps:
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\Helix\GenerateTestProjFile.ps1
|
||||
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 }}'
|
||||
arguments: -TestFile '${{ parameters.testFilePath }}' -OutputProjFile '$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\$(BuildPlatform)\${{ parameters.outputProjFileName }}' -JobTestSuiteName '${{ parameters.testSuite }}' -TaefPath '$(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.60.210621002\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'
|
||||
@@ -22,6 +22,7 @@ jobs:
|
||||
condition: succeededOrFailed()
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
HelixAccessToken: $(HelixApiAccessToken)
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\Helix\UpdateUnreliableTests.ps1
|
||||
@@ -32,6 +33,7 @@ jobs:
|
||||
condition: succeededOrFailed()
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
HelixAccessToken: $(HelixApiAccessToken)
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\Helix\OutputTestResults.ps1
|
||||
|
||||
@@ -15,6 +15,7 @@ parameters:
|
||||
# if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline:
|
||||
useBuildOutputFromPipeline: $(System.DefinitionId)
|
||||
openHelixTargetQueues: 'windows.10.amd64.client19h1.open.xaml'
|
||||
closedHelixTargetQueues: 'windows.10.amd64.client19h1.xaml'
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
@@ -29,11 +30,11 @@ jobs:
|
||||
buildConfiguration: ${{ parameters.configuration }}
|
||||
buildPlatform: ${{ parameters.platform }}
|
||||
openHelixTargetQueues: ${{ parameters.openHelixTargetQueues }}
|
||||
closedHelixTargetQueues: ${{ parameters.closedHelixTargetQueues }}
|
||||
artifactsDir: $(Build.SourcesDirectory)\Artifacts
|
||||
taefPath: $(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.58.210305002\build\Binaries\$(buildPlatform)
|
||||
taefPath: $(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.60.210621002\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}}'
|
||||
|
||||
|
||||
steps:
|
||||
- task: CmdLine@1
|
||||
displayName: 'Display build machine environment variables'
|
||||
@@ -140,6 +141,7 @@ jobs:
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Run tests in Helix (open queues)'
|
||||
condition: and(succeeded(),eq(variables['System.CollectionUri'],'https://dev.azure.com/ms/'))
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
inputs:
|
||||
@@ -148,3 +150,14 @@ jobs:
|
||||
custom: msbuild
|
||||
arguments: '$(helixCommonArgs) /p:IsExternal=true /p:Creator=Terminal /p:HelixTargetQueues=$(openHelixTargetQueues)'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Run tests in Helix (closed queues)'
|
||||
condition: and(succeeded(),ne(variables['System.CollectionUri'],'https://dev.azure.com/ms/'))
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
HelixAccessToken: $(HelixApiAccessToken)
|
||||
inputs:
|
||||
command: custom
|
||||
projects: build\Helix\RunTestsInHelix.proj
|
||||
custom: msbuild
|
||||
arguments: '$(helixCommonArgs) /p:HelixTargetQueues=$(closedHelixTargetQueues)'
|
||||
|
||||
@@ -20,11 +20,15 @@ jobs:
|
||||
inputs:
|
||||
artifactName: ${{ parameters.pgoArtifact }}
|
||||
downloadPath: $(artifactsPath)
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: 'Use NuGet 5.2.0'
|
||||
|
||||
- task: NuGetAuthenticate@0
|
||||
inputs:
|
||||
versionSpec: 5.2.0
|
||||
nuGetServiceConnections: 'Terminal Public Artifact Feed'
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: 'Use NuGet 5.8.0'
|
||||
inputs:
|
||||
versionSpec: 5.8.0
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy pgd files to NuGet build directory'
|
||||
@@ -58,5 +62,11 @@ jobs:
|
||||
displayName: 'NuGet push'
|
||||
inputs:
|
||||
command: push
|
||||
publishVstsFeed: Terminal/TerminalDependencies
|
||||
packagesToPush: $(Build.ArtifactStagingDirectory)/*.nupkg
|
||||
nuGetFeedType: external
|
||||
packagesToPush: $(Build.ArtifactStagingDirectory)/*.nupkg
|
||||
# The actual URL and PAT for this feed is configured at
|
||||
# https://microsoft.visualstudio.com/Dart/_settings/adminservices
|
||||
# This is the name of that connection
|
||||
publishFeedCredentials: 'Terminal Public Artifact Feed'
|
||||
feedsToUse: config
|
||||
nugetConfigPath: '$(Build.SourcesDirectory)/NuGet.config'
|
||||
@@ -1,74 +0,0 @@
|
||||
parameters:
|
||||
configuration: 'Release'
|
||||
|
||||
jobs:
|
||||
- job: SignDeploy${{ parameters.configuration }}
|
||||
displayName: Sign and Deploy for ${{ parameters.configuration }}
|
||||
|
||||
dependsOn:
|
||||
- Buildx64AuditMode
|
||||
- Buildx64Release
|
||||
- Buildx86Release
|
||||
- Buildarm64Release
|
||||
- CodeFormatCheck
|
||||
condition: |
|
||||
and
|
||||
(
|
||||
in(dependencies.Buildx64AuditMode.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildx64Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildx86Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildarm64Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.CodeFormatCheck.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')
|
||||
)
|
||||
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
AppxProjectName: CascadiaPackage
|
||||
AppxBundleName: Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle
|
||||
|
||||
pool:
|
||||
name: Package ES Lab E
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: 'Package ES - Setup Build'
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: WindowsTerminal
|
||||
disableOutputRedirect: true
|
||||
|
||||
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
|
||||
displayName: 'Component Detection'
|
||||
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download AppX artifacts
|
||||
inputs:
|
||||
artifactName: 'appx-$(BuildConfiguration)'
|
||||
itemPattern: |
|
||||
**/*.appx
|
||||
**/*.msix
|
||||
downloadPath: '$(Build.ArtifactStagingDirectory)\appx'
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Create $(AppxBundleName)'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: '.\build\scripts\Create-AppxBundle.ps1'
|
||||
arguments: |
|
||||
-InputPath "$(Build.ArtifactStagingDirectory)\appx" -ProjectName $(AppxProjectName) -BundleVersion 0.0.0.0 -OutputPath "$(Build.ArtifactStagingDirectory)\$(AppxBundleName)"
|
||||
|
||||
- task: PkgESCodeSign@10
|
||||
displayName: 'Package ES - SignConfig.WindowsTerminal.xml'
|
||||
inputs:
|
||||
signConfigXml: 'build\config\SignConfig.WindowsTerminal.xml'
|
||||
inPathRoot: '$(Build.ArtifactStagingDirectory)'
|
||||
outPathRoot: '$(Build.ArtifactStagingDirectory)\signed'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Signed AppX'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)\signed'
|
||||
ArtifactName: 'appxbundle-signed-$(BuildConfiguration)'
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2021</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>10</VersionMinor>
|
||||
<VersionMinor>12</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"title": "Microsoft's Windows Terminal Settings Profile Schema",
|
||||
"definitions": {
|
||||
"KeyChordSegment": {
|
||||
"pattern": "^(?<modifier>(?<mod1>ctrl|alt|shift|win)(?:\\+(?<mod2>ctrl|alt|shift|win)(?<!\\k<mod1>))?(?:\\+(?<mod3>ctrl|alt|shift|win)(?<!\\k<mod1>|\\k<mod2>))?(?:\\+(?<mod4>ctrl|alt|shift|win)(?<!\\k<mod1>|\\k<mod2>|\\k<mod3>))?\\+)?(?<key>[^\\s+]|app|menu|backspace|tab|enter|esc|escape|space|pgup|pageup|pgdn|pagedown|end|home|left|up|right|down|insert|delete|(?<!shift.+)(?:numpad_?[0-9]|numpad_(?:period|decimal))|numpad_(?:multiply|plus|add|minus|subtract|divide)|f[1-9]|f1[0-9]|f2[0-4]|plus)$",
|
||||
"pattern": "^(?:(?:ctrl|alt|shift|win)\\+)*(?:app|backspace|browser_(?:back|forward|refresh|stop|search|favorites|home)|comma|delete|down|end|enter|esc|escape|home|insert|left|menu|minus|pagedown|pageup|period|pgdn|pgup|plus|right|space|tab|up|f(?:1\\d?|2[0-4]?|[3-9])|numpad\\d|numpad_(?:\\d|add|decimal|divide|minus|multiply|period|plus|subtract)|(?:vk|sc)\\((?:[1-9]|1?\\d{2}|2[0-4]\\d|25[0-5])\\)|[^\\s+])(?:\\+(?:ctrl|alt|shift|win))*$",
|
||||
"type": "string",
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+][win+]<keyName>\", where each modifier is optional, separated by + symbols, and keyName is either one of the names listed in the table below, or any single key character. The string should be written in full lowercase.\napp, menu\tMENU key\nbackspace\tBACKSPACE key\ntab\tTAB key\nenter\tENTER key\nesc, escape\tESC key\nspace\tSPACEBAR\npgup, pageup\tPAGE UP key\npgdn, pagedown\tPAGE DOWN key\nend\tEND key\nhome\tHOME key\nleft\tLEFT ARROW key\nup\tUP ARROW key\nright\tRIGHT ARROW key\ndown\tDOWN ARROW key\ninsert\tINS key\ndelete\tDEL key\nnumpad_0-numpad_9, numpad0-numpad9\tNumeric keypad keys 0 to 9. Can't be combined with the shift modifier.\nnumpad_multiply\tNumeric keypad MULTIPLY key (*)\nnumpad_plus, numpad_add\tNumeric keypad ADD key (+)\nnumpad_minus, numpad_subtract\tNumeric keypad SUBTRACT key (-)\nnumpad_period, numpad_decimal\tNumeric keypad DECIMAL key (.). Can't be combined with the shift modifier.\nnumpad_divide\tNumeric keypad DIVIDE key (/)\nf1-f24\tF1 to F24 function keys\nplus\tADD key (+)"
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+][win+]<KeyName>\", where each modifier is optional. KeyName is either any single key character, an explicit virtual key or scan code in the form vk(nnn) and sc(nnn) respectively, or one of the special names listed at https://docs.microsoft.com/en-us/windows/terminal/customize-settings/actions#accepted-modifiers-and-keys"
|
||||
},
|
||||
"Color": {
|
||||
"default": "#",
|
||||
"pattern": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
|
||||
"pattern": "^#[A-Fa-f0-9]{3}(?:[A-Fa-f0-9]{3})?$",
|
||||
"type": "string",
|
||||
"format": "color"
|
||||
},
|
||||
@@ -154,6 +154,17 @@
|
||||
"description": "Sets how the background image aligns to the boundaries of the window when unfocused. Possible values: \"center\", \"left\", \"top\", \"right\", \"bottom\", \"topLeft\", \"topRight\", \"bottomLeft\", \"bottomRight\"",
|
||||
"type": "string"
|
||||
},
|
||||
"intenseTextStyle": {
|
||||
"default": "bright",
|
||||
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
|
||||
"enum": [
|
||||
"none",
|
||||
"bold",
|
||||
"bright",
|
||||
"all"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"experimental.retroTerminalEffect": {
|
||||
"description": "When set to true, enable retro terminal effects when unfocused. This is an experimental feature, and its continued existence is not guaranteed.",
|
||||
"type": "boolean"
|
||||
@@ -204,6 +215,22 @@
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"features": {
|
||||
"description": "Sets the DWrite font features for the given font. For example, { \"ss01\": 1, \"liga\":0 } will enable ss01 and disable ligatures.",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(([A-Za-z0-9]){4})$": { "type": "integer" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"axes": {
|
||||
"description": "Sets the DWrite font axes for the given font. For example, { \"wght\": 200 } will set the font weight to 200.",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^([A-Za-z]{4})$": { "type": "number" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -238,7 +265,10 @@
|
||||
"identifyWindow",
|
||||
"identifyWindows",
|
||||
"moveFocus",
|
||||
"movePane",
|
||||
"swapPane",
|
||||
"moveTab",
|
||||
"multipleActions",
|
||||
"newTab",
|
||||
"newWindow",
|
||||
"nextTab",
|
||||
@@ -249,6 +279,7 @@
|
||||
"paste",
|
||||
"prevTab",
|
||||
"renameTab",
|
||||
"openSystemMenu",
|
||||
"openTabRenamer",
|
||||
"quakeMode",
|
||||
"resetFontSize",
|
||||
@@ -270,9 +301,11 @@
|
||||
"toggleFocusMode",
|
||||
"toggleFullscreen",
|
||||
"togglePaneZoom",
|
||||
"toggleSplitOrientation",
|
||||
"toggleReadOnlyMode",
|
||||
"toggleShaderEffects",
|
||||
"wt",
|
||||
"quit",
|
||||
"unbound"
|
||||
],
|
||||
"type": "string"
|
||||
@@ -283,7 +316,10 @@
|
||||
"right",
|
||||
"up",
|
||||
"down",
|
||||
"previous"
|
||||
"previous",
|
||||
"nextInOrder",
|
||||
"previousInOrder",
|
||||
"first"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -491,6 +527,23 @@
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Which tab to switch to, with the first being 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "index" ]
|
||||
},
|
||||
"MovePaneAction": {
|
||||
"description": "Arguments corresponding to a Move Pane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "movePane" },
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Which tab to move the pane to, with the first being 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -507,7 +560,24 @@
|
||||
"direction": {
|
||||
"$ref": "#/definitions/FocusDirection",
|
||||
"default": "left",
|
||||
"description": "The direction to move focus in, between panes. Direction can be 'previous' to move to the most recently used pane."
|
||||
"description": "The direction to move focus in, between panes. Direction can be 'previous' to move to the most recently used pane, or 'nextInOrder' or 'previousInOrder' to move to the next or previous pane."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"SwapPaneAction": {
|
||||
"description": "Arguments corresponding to a Swap Pane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "swapPane" },
|
||||
"direction": {
|
||||
"$ref": "#/definitions/FocusDirection",
|
||||
"default": "left",
|
||||
"description": "The direction to move the focus pane in, swapping panes. Direction can be 'previous' to swap with the most recently used pane, or 'nextInOrder' or 'previousInOrder' to move to the next or previous pane."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -596,7 +666,7 @@
|
||||
"defaultsFile",
|
||||
"allFiles",
|
||||
"settingsUI"
|
||||
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -758,6 +828,24 @@
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"MultipleActionsAction": {
|
||||
"description": "Arguments for the multiple actions command",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "multipleActions" },
|
||||
"actions" : {
|
||||
"$ref": "#/definitions/ShortcutAction",
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"description": "A list of other actions."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "actions" ]
|
||||
},
|
||||
"CommandPaletteAction": {
|
||||
"description": "Arguments for a commandPalette action",
|
||||
"allOf": [
|
||||
@@ -952,6 +1040,8 @@
|
||||
{ "$ref": "#/definitions/NewTabAction" },
|
||||
{ "$ref": "#/definitions/SwitchToTabAction" },
|
||||
{ "$ref": "#/definitions/MoveFocusAction" },
|
||||
{ "$ref": "#/definitions/MovePaneAction" },
|
||||
{ "$ref": "#/definitions/SwapPaneAction" },
|
||||
{ "$ref": "#/definitions/ResizePaneAction" },
|
||||
{ "$ref": "#/definitions/SendInputAction" },
|
||||
{ "$ref": "#/definitions/SplitPaneAction" },
|
||||
@@ -1116,6 +1206,10 @@
|
||||
"description": "When set to true, we will use the software renderer (a.k.a. WARP) instead of the hardware one.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.input.forceVT": {
|
||||
"description": "Force the terminal to use the legacy input encoding. Certain keys in some applications may stop working when enabling this setting.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"initialCols": {
|
||||
"default": 120,
|
||||
"description": "The number of columns displayed in the window upon first load. If \"launchMode\" is set to \"maximized\" (or \"maximizedFocus\"), this property is ignored.",
|
||||
@@ -1139,6 +1233,15 @@
|
||||
"description": "When set to true, this enables the launch of Windows Terminal at startup. Setting this to false will disable the startup task entry. If the Windows Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"firstWindowPreference": {
|
||||
"default": "defaultProfile",
|
||||
"description": "Defines what behavior the terminal takes when it starts. \"defaultProfile\" will have the terminal launch with one tab of the default profile, and \"persistedWindowLayout\" will cause the terminal to save its layout on close and reload it on open.",
|
||||
"enum": [
|
||||
"defaultProfile",
|
||||
"persistedWindowLayout"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"launchMode": {
|
||||
"default": "default",
|
||||
"description": "Defines whether the terminal will launch as maximized, full screen, or in a window. Setting this to \"focus\" is equivalent to launching the terminal in the \"default\" mode, but with the focus mode enabled. Similar, setting this to \"maximizedFocus\" will result in launching the terminal in a maximized window with the focus mode enabled.",
|
||||
@@ -1158,6 +1261,21 @@
|
||||
"minimum": 0,
|
||||
"type": [ "integer", "string" ],
|
||||
"deprecated": true
|
||||
},
|
||||
"minimizeToTray": {
|
||||
"default": "false",
|
||||
"description": "When set to true, minimizing a Terminal window will no longer appear in the taskbar. Instead, a Terminal icon will appear in the system tray through which the user can access their windows.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"alwaysShowTrayIcon": {
|
||||
"default": "false",
|
||||
"description": "When set to true, the Terminal's tray icon will always be shown in the system tray.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"useAcrylicInTabRow": {
|
||||
"default": "false",
|
||||
"description": "When set to true, the tab row will have an acrylic background with 50% opacity.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"actions": {
|
||||
"description": "Properties are specific to each custom action.",
|
||||
|
||||
@@ -0,0 +1,619 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2020-11-20
|
||||
last updated: 2021-08-17
|
||||
issue id: #1032
|
||||
---
|
||||
|
||||
# Elevation Quality of Life Improvements
|
||||
|
||||
## Abstract
|
||||
|
||||
For a long time, we've been researching adding support to the Windows Terminal
|
||||
for running both unelevated and elevated (admin) tabs side-by-side, in the same
|
||||
window. However, after much research, we've determined that there isn't a safe
|
||||
way to do this without opening the Terminal up as a potential
|
||||
escalation-of-privilege vector.
|
||||
|
||||
Instead, we'll be adding a number of features to the Terminal to improve the
|
||||
user experience of working in elevated scenarios. These improvements include:
|
||||
|
||||
* A visible indicator that the Terminal window is elevated ([#1939])
|
||||
* Configuring the Terminal to always run elevated ([#632])
|
||||
* Configuring a specific profile to always open elevated ([#632])
|
||||
* Allowing new tabs, panes to be opened elevated directly from an unelevated
|
||||
window
|
||||
* Dynamic profile appearance that changes depending on if the Terminal is
|
||||
elevated or not. ([#1939], [#8311])
|
||||
|
||||
## Background
|
||||
|
||||
_This section was originally authored in the [Process Model 2.0 Spec]. Please
|
||||
refer to it there for its original context._
|
||||
|
||||
Let's presume that you're a user who wants to be able to open an elevated tab
|
||||
within an otherwise unelevated Terminal window. We call this scenario "mixed
|
||||
elevation" - the tabs within the Terminal can be running either unelevated _or_
|
||||
elevated client applications.
|
||||
|
||||
It wouldn't be terribly difficult for the unelevated Terminal to request the
|
||||
permission of the user to spawn an elevated client application. The user would
|
||||
see a UAC prompt, they'd accept, and then they'd be able to have an elevated
|
||||
shell alongside their unelevated tabs.
|
||||
|
||||
However, this creates an escalation of privilege vector. Now, there's an
|
||||
unelevated window which is connected directly to an elevated process. At this
|
||||
point, **any other unelevated application could send input to the Terminal's
|
||||
`HWND`**. This would make it possible for another unelevated process to "drive"
|
||||
the Terminal window, and send commands to the elevated client application.
|
||||
|
||||
It was initially theorized that the window/content model architecture would also
|
||||
help enable "mixed elevation". With mixed elevation, tabs could run at different
|
||||
integrity levels within the same terminal window. However, after investigation
|
||||
and research, it has become apparent that this scenario is not possible to do
|
||||
safely after all. There are numerous technical difficulties involved, and each
|
||||
with their own security risks. At the end of the day, the team wouldn't be
|
||||
comfortable shipping a mixed-elevation solution, because there's simply no way
|
||||
for us to be confident that we haven't introduced an escalation-of-privilege
|
||||
vector utilizing the Terminal. No matter how small the attack surface might be,
|
||||
we wouldn't be confident that there are _no_ vectors for an attack.
|
||||
|
||||
Some things we considered during this investigation:
|
||||
|
||||
* If a user requests a new elevated tab from an otherwise unelevated window, we
|
||||
could use UAC to create a new, elevated window process, and "move" all the
|
||||
current tabs to that window process, as well as the new elevated client. Now,
|
||||
the window process would be elevated, preventing it from input injection, and
|
||||
it would still contains all the previously existing tabs. The original window
|
||||
process could now be discarded, as the new elevated window process will
|
||||
pretend to be the original window.
|
||||
- However, it is unfortunately not possible with COM to have an elevated
|
||||
client attach to an unelevated server that's registered at runtime. Even in
|
||||
a packaged environment, the OS will reject the attempt to `CoCreateInstance`
|
||||
the content process object. this will prevent elevated windows from
|
||||
re-connecting to unelevated client processes.
|
||||
- We could theoretically build an RPC tunnel between content and window
|
||||
processes, and use the RPC connection to marshal the content process to the
|
||||
elevated window. However, then _we_ would need to be responsible for
|
||||
securing access the the RPC endpoint, and we feel even less confident doing
|
||||
that.
|
||||
- Attempts were also made to use a window-broker-content architecture, with
|
||||
the broker process having a static CLSID in the registry, and having the
|
||||
window and content processes at mixed elevation levels `CoCreateInstance`
|
||||
that broker. This however _also_ did not work across elevation levels. This
|
||||
may be due to a lack of Packaged COM support for mixed elevation levels.
|
||||
|
||||
It's also possible that the author forgot that packaged WinRT doesn't play
|
||||
nicely with creating objects in an elevated context. The Terminal has
|
||||
previously needed to manually manifest all its classes in a SxS manifest for
|
||||
Unpackaged WinRT to allow the classes to be activated, rather than relying
|
||||
on the packaged catalog. It's theoretically possible that doing that would
|
||||
have allowed the broker to be activated across integrity levels.
|
||||
|
||||
Even if this approach did end up working, we would still need to be
|
||||
responsible for securing the elevated windows so that an unelevated attacker
|
||||
couldn't hijack a content process and trigger unexpected code in the window
|
||||
process. We didn't feel confident that we could properly secure this channel
|
||||
either.
|
||||
|
||||
We also considered allowing mixed content in windows that were _originally_
|
||||
elevated. If the window is already elevated, then it can launch new unelevated
|
||||
processes. We could allow elevated windows to still create unelevated
|
||||
connections. However, we'd want to indicate per-pane what the elevation state
|
||||
of each connection is. The user would then need to keep track themselves of
|
||||
which terminal instances are elevated, and which are not.
|
||||
|
||||
This also marks a departure from the current behavior, where everything in an
|
||||
elevated window would be elevated by default. The user would need to specify for
|
||||
each thing in the elevated window that they'd want to create it elevated. Or the
|
||||
Terminal would need to provide some setting like
|
||||
`"autoElevateEverythingInAnElevatedWindow"`.
|
||||
|
||||
We cannot support mixed elevation when starting in a unelevated window.
|
||||
Therefore, it doesn't make a lot of UX sense to support it in the other
|
||||
direction. It's a cleaner UX story to just have everything in a single window at
|
||||
the same elevation level.
|
||||
|
||||
## Solution Design
|
||||
|
||||
Instead of supporting mixed elevation in the same window, we'll introduce the
|
||||
following features to the Terminal. These are meant as a way of improving the
|
||||
quality of life for users who work in mixed-elevation (or even just elevated)
|
||||
environments.
|
||||
|
||||
### Visible indicator for elevated windows
|
||||
|
||||
As requested in [#1939], it would be nice if it was easy to visibly identify if
|
||||
a Terminal window was elevated or not.
|
||||
|
||||
One easy way of doing this is by adding a simple UAC shield to the left of the
|
||||
tabs for elevated windows. This shield could be configured by the theme (see
|
||||
[#3327]). We could provide the following states:
|
||||
* Colored (the default)
|
||||
* Monochrome
|
||||
* Hidden, to hide the shield even on elevated windows. This is the current
|
||||
behavior.
|
||||
|
||||

|
||||
_figure 1: a monochrome UAC shield in the titlebar of the window, courtesy of @mdtauk_
|
||||
|
||||
We could also simplify this to only allow a boolean true/false for displaying
|
||||
the shield. As we do often with other enums, we could define `true` to be the
|
||||
same as the default appearance, and `false` to be the hidden option. As always,
|
||||
the development of the Terminal is an iterative process, where we can
|
||||
incrementally improve from no setting, to a boolean setting, to a enum-backed
|
||||
one.
|
||||
|
||||
### Configuring a profile to always run elevated
|
||||
|
||||
Oftentimes, users might have a particular tool chain that only works when
|
||||
running elevated. In these scenarios, it would be convenient for the user to be
|
||||
able to identify that the profile should _always_ run elevated. That way, they
|
||||
could open the profile from the dropdown menu of an otherwise unelevated window
|
||||
and have the elevated window open with the profile automatically.
|
||||
|
||||
We'll be adding the `"elevate": true|false` setting as a per-profile setting,
|
||||
with a default value of `false`. When set to `true`, we'll try to auto-elevate
|
||||
the profile whenever it's launched. We'll check to see if this window is
|
||||
elevated before creating the connection for this profile. If the window is not
|
||||
elevated, then we'll create a new window with the requested elevation level to
|
||||
handle the new connection.
|
||||
|
||||
`"elevate": false` will do nothing. If the window is already elevated, then the
|
||||
profile won't open an un-elevated window.
|
||||
|
||||
If the user tries to open an `"elevate": true` profile in a window that's
|
||||
already elevated, then a new tab/split will open in the existing window, rather
|
||||
than spawning an additional elevated window.
|
||||
|
||||
There are three situations where we're creating new terminal instances: new
|
||||
tabs, new splits, and new windows. Currently, these are all actions that are
|
||||
also exposed in the `wt` commandline as subcommands. We can convert from the
|
||||
commandline arguments into these actions already. Therefore, it shouldn't be too
|
||||
challenging to convert these actions back into the equal commandline arguments.
|
||||
|
||||
For the following examples, let's assume the user is currently in an unelevated
|
||||
Terminal window.
|
||||
|
||||
When the user tries to create a new elevated **tab**, we'll need to create a new
|
||||
process, elevated, with the following commandline:
|
||||
|
||||
```
|
||||
wt new-tab [args...]
|
||||
```
|
||||
|
||||
When we create this new `wt` instance, it will obey the glomming rules as
|
||||
specified in [Session Management Spec]. It might end up glomming to another
|
||||
existing window at that elevation level, or possibly create its own window.
|
||||
|
||||
Similarly, for a new elevated **window**, we can make sure to pass the `-w new`
|
||||
arguments to `wt`. These parameters indicate that we definitely want this
|
||||
command to run in a new window, regardless of the current glomming settings.
|
||||
|
||||
```
|
||||
wt -w new new-tab [args...]
|
||||
```
|
||||
|
||||
However, creating a new **pane** is a little trickier. Invoking the `wt
|
||||
split-pane [args...]` is straightforward enough.
|
||||
|
||||
<!-- Discussion notes follow:
|
||||
If the current window doesn't have the same elevation level as the
|
||||
requested profile, do we always want to just create a new split? If the command
|
||||
ends up glomming to an existing window, does that even make sense? That invoking
|
||||
an elevated split in an unelevated window would end up splitting the elevated
|
||||
window? It's very possible that the user wanted a split in the tab they're
|
||||
currently in, in the unelevated window, but they don't want a split in the
|
||||
elevated window.
|
||||
|
||||
What if there's not space in the elevated window to create the split (but there
|
||||
would be in the current window)? That would sure make it seem like nothing
|
||||
happened, silently.
|
||||
|
||||
We could alternatively have cross-elevation splits default to always opening a
|
||||
new tab. That might mitigate some of the odd behaviors. Until we actually have
|
||||
support for running commands in existing windows, we'll always need to make a
|
||||
new window when running elevated. We'll need to make the new window for new tabs
|
||||
and splits, because there's no way to invoke another existing window.
|
||||
|
||||
A third proposal is to pop a warning dialog at the user when they try to open an
|
||||
elevated split from and unelevated window. This dialog could be something like
|
||||
|
||||
> What you requested couldn't be completed. Do you want to:
|
||||
> A. Make me a new tab instead.
|
||||
> B. Forget it and cancel. I'll go fix my config.
|
||||
|
||||
I'm certainly leaning towards proposal 2 - always create a new tab. This is how
|
||||
it's implemented in [#8514]. In that PR, this seems to work sensibly.
|
||||
-->
|
||||
|
||||
After discussing with the team, we have decided that the most sensible approach
|
||||
for handling a cross-elevation `split-pane` is to just create a new tab in the
|
||||
elevated window. The user can always re-attach the pane as a split with the
|
||||
`move-pane` command once the new pane in the elevated window.
|
||||
|
||||
#### Configure the Terminal to _always_ run elevated
|
||||
|
||||
`elevate` is a per-profile property, not a global property. If a user
|
||||
wants to always have all instances of the Terminal run elevated, they
|
||||
could set `"elevate": true` in their profile defaults. That would cause _all_
|
||||
profiles they launch to always spawn as elevated windows.
|
||||
|
||||
#### `elevate` in Actions
|
||||
|
||||
Additionally, we'll add the `elevate` property to the `NewTerminalArgs` used in
|
||||
the `newTab`, `splitPane`, and `newWindow` actions. This is similar to how other
|
||||
properties of profiles can be overridden at launch time. This will allow
|
||||
windows, tabs and panes to all be created specifically as elevated windows.
|
||||
|
||||
In the `NewTerminalArgs`, `elevate` will be an optional boolean, with the
|
||||
following behavior:
|
||||
* `null` (_default_): Don't modify the `elevate` property for this profile
|
||||
* `true`: This launch should act like the profile had `"elevate": true` in its
|
||||
properties.
|
||||
* `false`: This launch should act like the profile had `"elevate": false` in its
|
||||
properties.
|
||||
|
||||
We'll also add an iterable command for opening a profile in an
|
||||
elevated tab, with the following json:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
// New elevated tab...
|
||||
"name": { "key": "NewElevatedTabParentCommandName", "icon": "UAC-Shield.png" },
|
||||
"commands": [
|
||||
{
|
||||
"iterateOn": "profiles",
|
||||
"icon": "${profile.icon}",
|
||||
"name": "${profile.name}",
|
||||
"command": { "action": "newTab", "profile": "${profile.name}", "elevated": true }
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
#### Elevation from the dropdown
|
||||
|
||||
Currently, the new tab dropdown supports opening a new pane by
|
||||
<kbd>Alt+click</kbd>ing on a profile. We could similarly add support to open a
|
||||
tab elevated with <kbd>Ctrl+click</kbd>. This is similar to the behavior of the
|
||||
Windows taskbar. It supports creating an elevated instance of a program by
|
||||
<kbd>Ctrl+click</kbd>ing on entries as well.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Starting an elevated process from an unelevated process
|
||||
|
||||
It seems that we're able to create an elevated process by passing the `"runas"`
|
||||
verb to
|
||||
[`ShellExecute`](https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutea).
|
||||
So we could use something like
|
||||
|
||||
```c++
|
||||
ShellExecute(nullptr,
|
||||
L"runas",
|
||||
L"wt.exe",
|
||||
L"-w new new-tab [args...]",
|
||||
nullptr,
|
||||
SW_SHOWNORMAL);
|
||||
```
|
||||
|
||||
This will ask the shell to perform a UAC prompt before spawning `wt.exe` as an
|
||||
elevated process.
|
||||
|
||||
> 👉 NOTE: This mechanism won't always work on non-Desktop SKUs of Windows. For
|
||||
> more discussion, see [Elevation on OneCore SKUs](#Elevation-on-OneCore-SKUs).
|
||||
|
||||
## Potential Issues
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><strong>Accessibility</strong></td>
|
||||
<td>
|
||||
|
||||
The set of changes proposed here are not expected to introduce any new
|
||||
accessibility issues. Users can already create elevated Terminal windows. Making
|
||||
it easier to create these windows doesn't really change our accessibility story.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Security</strong></td>
|
||||
<td>
|
||||
|
||||
We won't be doing anything especially unique, so there aren't expected to be any
|
||||
substantial security risks associated with these changes. Users can already
|
||||
create elevated Terminal windows, so we're not really introducing any new
|
||||
functionality, from a security perspective.
|
||||
|
||||
We're relying on the inherent security of the `runas` verb of `ShellExecute` to
|
||||
prevent any sort of unexpected escalation-of-privilege.
|
||||
|
||||
<hr>
|
||||
|
||||
One security concern is the fact that the `settings.json` file is currently a
|
||||
totally unsecured file. It's completely writable by any medium-IL process. That
|
||||
means it's totally possible for a malicious program to change the file. The
|
||||
malicious program could find a user's "Elevated PowerShell" profile, and change
|
||||
the commandline to `malicious.exe`. The user might then think that their
|
||||
"Elevated PowerShell" will run `powershell.exe` elevated, but will actually
|
||||
auto-elevate this attacker.
|
||||
|
||||
If all we expose to the user is the name of the profile in the UAC dialog, then
|
||||
there's no way for the user to be sure that the program that's about to be
|
||||
launched is actually what they expect.
|
||||
|
||||
To help mitigate this, we should _always_ pass the evaluated `commandline` as a
|
||||
part of the call to `ShellExecute`. the arguments that are passed to
|
||||
`ShellExecute` are visible to the user, though they need to click the "More
|
||||
Details" dropdown to reveal them.
|
||||
|
||||
We will need to mitigate this vulnerability regardless of adding support for the
|
||||
auto-elevation of individual terminal tabs/panes. If a user is launching the
|
||||
Terminal elevated (i.e. from the Win+X menu in Windows 11), then it's possible
|
||||
for a malicious program to overwrite the `commandline` of their default profile.
|
||||
The user may now unknowingly invoke this malicious program while thinking they
|
||||
are simply launching the Terminal.
|
||||
|
||||
To deal with this more broadly, we will display a dialog within the Terminal
|
||||
window before creating **any** elevated terminal instance. In that dialog, we'll
|
||||
display the commandline that will be executed, so the user can very easily
|
||||
confirm the commandline.
|
||||
|
||||
This will need to happen for all elevated terminal instances. For an elevated
|
||||
Windows Terminal window, this means _all_ connections made by the Terminal.
|
||||
Every time the user opens a new profile or a new commandline in a pane, we'll
|
||||
need to prompt them first to confirm the commandline. This dialog within the
|
||||
elevated window will also prevent an attacker from editing the `settings.json`
|
||||
file while the user already has an elevated Terminal window open and hijacking a
|
||||
profile.
|
||||
|
||||
The dialog options will certainly be annoying to users who don't want to be
|
||||
taken out of their flow to confirm the commandline that they wish to launch.
|
||||
There's precedent for a similar warning being implemented by VSCode, with their
|
||||
[Workspace Trust] feature. They too faced a similar backlash when the feature
|
||||
first shipped. However, in light of recent global cybersecurity attacks, this is
|
||||
seen as an acceptable UX degradation in the name of application trust. We don't
|
||||
want to provide an avenue that's too easy to abuse.
|
||||
|
||||
When the user confirms the commandline of this profile as something safe to run,
|
||||
we'll add it to an elevated-only version of `state.json`. (see [#7972] for more
|
||||
details). This elevated version of the file will only be accessible by the
|
||||
elevated Terminal, so an attacker cannot hijack the contents of the file. This
|
||||
will help mitigate the UX discomfort caused by prompting on every commandline
|
||||
launched. This should mean that the discomfort is only limited to the first
|
||||
elevated launch of a particular profile. Subsequent launches (without modifying
|
||||
the `commandline`) will work as they always have.
|
||||
|
||||
The dialog for confirming these commandlines should have a link to the docs for
|
||||
"Learn more...". Transparency in the face of this dialog should
|
||||
mitigate some dissatisfaction.
|
||||
|
||||
The dialog will _not_ appear if the user does not have a split token - if the
|
||||
user's PC does not have UAC enabled, then they're _already_ running as an
|
||||
Administrator. Everything they do is elevated, so they shouldn't be prompted in
|
||||
this way.
|
||||
|
||||
The Settings UI should also expose a way of viewing and removing these cached
|
||||
entries. This page should only be populated in the elevated version of the
|
||||
Terminal.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Reliability</strong></td>
|
||||
<td>
|
||||
|
||||
No changes to our reliability are expected as a part of this change.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Compatibility</strong></td>
|
||||
<td>
|
||||
|
||||
There are no serious compatibility concerns expected with this changelist. The
|
||||
new `elevate` property will be unset by default, so users will heed to opt-in
|
||||
to the new auto-elevating behavior.
|
||||
|
||||
There is one minor concern regarding introducing the UAC shield on the window.
|
||||
We're planning on using themes to configure the appearance of the shield. That
|
||||
means we'll need to ship themes before the user will be able to hide the shield
|
||||
again.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Performance, Power, and Efficiency</strong></td>
|
||||
<td>
|
||||
|
||||
No changes to our performance are expected as a part of this change.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Centennial Applications
|
||||
|
||||
In the past, we've had a notoriously rough time with the Centennial app
|
||||
infrastructure and running the Terminal elevated. Notably, we've had to list all
|
||||
our WinRT classes in our SxS manifest so they could be activated using
|
||||
unpackaged WinRT while running elevated. Additionally, there are plenty of
|
||||
issues running the Terminal in an "over the shoulder" elevation (OTS) scenario.
|
||||
|
||||
Specifically, we're concerned with the following scenario:
|
||||
* the current user account has the Terminal installed,
|
||||
* but they aren't an Administrator,
|
||||
* the Administrator account doesn't have the Terminal installed.
|
||||
|
||||
In that scenario, the user can run into issues launching the Terminal in an
|
||||
elevated context (even after entering the Admin's credentials in the UAC
|
||||
prompt).
|
||||
|
||||
This spec proposes no new mitigations for dealing with these issues. It may in
|
||||
fact make them more prevalent, by making elevated contexts more easily
|
||||
accessible.
|
||||
|
||||
Unfortunately, these issues are OS bugs that are largely out of our own control.
|
||||
We will continue to apply pressure to the centennial app team internally as we
|
||||
encounter these issues. They are are team best equipped to resolve these issues.
|
||||
|
||||
### Default Terminal & auto-elevation
|
||||
|
||||
In the future, when we support setting the Terminal as the "default terminal
|
||||
emulator" on Windows. When that lands, we will use the `profiles.defaults`
|
||||
settings to create the tab where we'll be hosting the commandline client. If the user has
|
||||
`"elevate": true` in their `profiles.defaults`, we'd usually try to
|
||||
auto-elevate the profile. In this scenario, however, we can't do that. The
|
||||
Terminal is being invoked on behalf of the client app launching, instead of the
|
||||
Terminal invoking the client application.
|
||||
|
||||
**2021-08-17 edit**: Now that "defterm" has shipped, we're a little more aware
|
||||
of some of the limitations with packaged COM and elevation boundaries. Defterm
|
||||
cannot be used with elevated processes _at all_ currently (see [#10276]). When
|
||||
an elevated commandline application is launched, it will always just appear in
|
||||
`conhost.exe`. Furthermore, An unelevated peasant can't communicate with an
|
||||
elevated monarch so we can't toss the connection to the elevated monarch and
|
||||
have them handle it.
|
||||
|
||||
The simplest solution here is to just _always_ ignore the `elevate` property for
|
||||
incoming defterm connections. This is not an ideal solution, and one that we're
|
||||
willing to revisit if/when [#10276] is ever fixed.
|
||||
|
||||
### Elevation on OneCore SKUs
|
||||
|
||||
This spec proposes using `ShellExecute` to elevate the Terminal window. However,
|
||||
not all Windows SKUs have support for `ShellExecute`. Notably, the non-Desktop
|
||||
SKUs, which are often referred to as "OneCore" SKUs. On these platforms, we
|
||||
won't be able to use `ShellExecute` to elevate the Terminal. There might not
|
||||
even be the concept of multiple elevation levels, or different users, depending
|
||||
on the SKU.
|
||||
|
||||
Fortunately, this is a mostly hypothetical concern for the moment. Desktop is
|
||||
the only publicly supported SKU for the Terminal currently. If the Terminal ever
|
||||
does become available on those SKUs, we can use these proposals as mitigations.
|
||||
|
||||
* If elevation is supported, there must be some other way of elevating a
|
||||
process. We could always use that mechanism instead.
|
||||
* If elevation isn't supported (I'm thinking 10X is one of these), then we could
|
||||
instead display a warning dialog whenever a user tries to open an elevated
|
||||
profile.
|
||||
- We could take the warning a step further. We could add another settings
|
||||
validation step. This would warn the user if they try to mark any profiles
|
||||
or actions as `"elevate":true`
|
||||
|
||||
## Future considerations
|
||||
|
||||
* If we wanted to go even further down the visual differentiation route, we
|
||||
could consider allowing the user to set an entirely different theme ([#3327])
|
||||
based on the elevation state. Something like `elevatedTheme`, to pick another
|
||||
theme from the set of themes. This would allow them to force elevated windows
|
||||
to have a red titlebar, for example.
|
||||
* Over the course of discussion concerning appearance objects ([#8345]), it
|
||||
became clear that having separate "elevated" appearances defined for
|
||||
`profile`s was overly complicated. This is left as a consideration for a
|
||||
possible future extension that could handle this scenario in a cleaner way.
|
||||
* Similarly, we're going to leave [#3637] "different profiles when elevated vs
|
||||
unelevated" for the future. This also plays into the design of "configure the
|
||||
new tab dropdown" ([#1571]), and reconciling those two designs is out-of-scope
|
||||
for this particular release.
|
||||
* Tangentially, we may want to have a separate Terminal icon we ship with the
|
||||
UAC shield present on it. This would be especially useful for the tray icon.
|
||||
Since there will be different tray icon instances for elevated and unelevated
|
||||
windows, having unique icons may help users identify which is which.
|
||||
|
||||
### De-elevating a Terminal
|
||||
|
||||
the original version of this spec proposed that `"elevated":false` from an
|
||||
elevated Terminal window should create a new unelevated Terminal instance. The
|
||||
mechanism for doing this is described in [The Old New Thing: How can I launch an
|
||||
unelevated process from my elevated process, redux].
|
||||
|
||||
This works well when the Terminal is running unpackaged. However, de-elevating a
|
||||
process does not play well with packaged centennial applications. When asking
|
||||
the OS to run the packaged application from an elevated context, the system will
|
||||
still create the child process _elevated_. This means the packaged version of
|
||||
the Terminal won't be able to create a new unelevated Terminal instance.
|
||||
|
||||
From an internal mail thread:
|
||||
|
||||
> App model intercepts the `CreateProcess` call and redirects it to a COM
|
||||
> service. The parent of a packaged app is not the launching app, it’s some COM
|
||||
> service. So none of the parent process nonsense will work because the
|
||||
> parameters you passed to `CreateProcess` aren’t being used to create the
|
||||
> process.
|
||||
|
||||
If this is fixed in the future, we could theoretically re-introduce de-elevating
|
||||
a profile. The original spec proposed a `"elevated": bool?` setting, with the
|
||||
following behaviors:
|
||||
* `null` (_default_): Don't modify the elevation level when running this profile
|
||||
* `true`: If the current window is unelevated, try to create a new elevated
|
||||
window to host this connection.
|
||||
* `false`: If the current window is elevated, try to create a new unelevated
|
||||
window to host this connection.
|
||||
|
||||
We could always re-introduce this setting, to supercede `elevate`.
|
||||
|
||||
### Change profile appearance for elevated windows
|
||||
|
||||
In [#3062] and [#8345], we're planning on allowing users to set different
|
||||
appearances for a profile whether it's focused or not. We could do similar thing
|
||||
to enable a profile to have a different appearance when elevated. In the
|
||||
simplest case, this could allow the user to set `"background": "#ff0000"`. This
|
||||
would make a profile always appear to have a red background when in an elevated
|
||||
window.
|
||||
|
||||
The more specific details of this implementation are left to the spec
|
||||
[Configuration object for profiles].
|
||||
|
||||
In discussion of that spec, we decided that it would be far too complicated to
|
||||
try and overload the `unfocusedAppearance` machinery for differentiating between
|
||||
elevated and unelevated versions of the same profile. Already, that would lead
|
||||
to 4 states: [`appearance`, `unfocusedAppearance`, `elevatedAppearance`,
|
||||
`elevatedUnfocusedAppearance`]. This would lead to a combinatorial explosion if
|
||||
we decided in the future that there should also be other states for a profile.
|
||||
|
||||
This particular QoL improvement is currently being left as a future
|
||||
consideration, should someone come up with a clever way of defining
|
||||
elevated-specific settings.
|
||||
|
||||
<!--
|
||||
Brainstorming notes for future readers:
|
||||
|
||||
You could have a profile that layers on an existing profile, with elevated-specific settings:
|
||||
|
||||
{
|
||||
"name": "foo",
|
||||
"background": "#0000ff",
|
||||
"commandline": "cmd.exe /k echo I am unelevated"
|
||||
},
|
||||
{
|
||||
"inheritsFrom": "foo",
|
||||
"background": "#ff0000",
|
||||
"elevate": true,
|
||||
"commandline": "cmd.exe /k echo I am ELEVATED"
|
||||
}
|
||||
-->
|
||||
|
||||
<!-- Footnotes -->
|
||||
|
||||
[#632]: https://github.com/microsoft/terminal/issues/632
|
||||
[#1032]: https://github.com/microsoft/terminal/issues/1032
|
||||
[#1571]: https://github.com/microsoft/terminal/issues/1571
|
||||
[#1939]: https://github.com/microsoft/terminal/issues/1939
|
||||
[#3062]: https://github.com/microsoft/terminal/issues/3062
|
||||
[#3327]: https://github.com/microsoft/terminal/issues/3327
|
||||
[#3637]: https://github.com/microsoft/terminal/issues/3637
|
||||
[#4472]: https://github.com/microsoft/terminal/issues/4472
|
||||
[#5000]: https://github.com/microsoft/terminal/issues/5000
|
||||
[#7972]: https://github.com/microsoft/terminal/pull/7972
|
||||
[#8311]: https://github.com/microsoft/terminal/issues/8311
|
||||
[#8345]: https://github.com/microsoft/terminal/issues/8345
|
||||
[#8514]: https://github.com/microsoft/terminal/issues/8514
|
||||
[#10276]: https://github.com/microsoft/terminal/issues/10276
|
||||
|
||||
[Process Model 2.0 Spec]: https://github.com/microsoft/terminal/blob/main/doc/specs/%235000%20-%20Process%20Model%202.0.md
|
||||
[Configuration object for profiles]: https://github.com/microsoft/terminal/blob/main/doc/specs/Configuration%20object%20for%20profiles.md
|
||||
[Session Management Spec]: https://github.com/microsoft/terminal/blob/main/doc/specs/%234472%20-%20Windows%20Terminal%20Session%20Management.md
|
||||
[The Old New Thing: How can I launch an unelevated process from my elevated process, redux]: https://devblogs.microsoft.com/oldnewthing/20190425-00/?p=102443
|
||||
[Workspace Trust]: https://code.visualstudio.com/docs/editor/workspace-trust
|
||||
BIN
doc/specs/#5000 - Process Model 2.0/UAC-shield-in-titlebar.png
Normal file
BIN
doc/specs/#5000 - Process Model 2.0/UAC-shield-in-titlebar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 565 KiB |
BIN
doc/specs/#6900 - Actions Page/add-click.png
Normal file
BIN
doc/specs/#6900 - Actions Page/add-click.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
BIN
doc/specs/#6900 - Actions Page/add-keys.png
Normal file
BIN
doc/specs/#6900 - Actions Page/add-keys.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
BIN
doc/specs/#6900 - Actions Page/edit-click.png
Normal file
BIN
doc/specs/#6900 - Actions Page/edit-click.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
BIN
doc/specs/#6900 - Actions Page/edit-keys.png
Normal file
BIN
doc/specs/#6900 - Actions Page/edit-keys.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
130
doc/specs/#6900 - Actions Page/spec.md
Normal file
130
doc/specs/#6900 - Actions Page/spec.md
Normal file
@@ -0,0 +1,130 @@
|
||||
---
|
||||
author: Kayla Cinnamon - cinnamon-msft
|
||||
created on: 2021-03-04
|
||||
last updated: 2021-03-09
|
||||
issue id: 6900
|
||||
---
|
||||
|
||||
# Actions Page
|
||||
|
||||
## Abstract
|
||||
|
||||
We need to represent actions inside the settings UI. This spec goes through the possible use cases and reasoning for including specific features for actions inside the settings UI.
|
||||
|
||||
## Background
|
||||
|
||||
### Inspiration
|
||||
|
||||
It would be ideal if we could get the settings UI to have parity with the JSON file. This will take some design work if we want every feature possible in relation to actions. There is also the option of not having parity with the JSON file in order to present a simpler UX.
|
||||
|
||||
### User Stories
|
||||
|
||||
All of these features are possible with the JSON file. This spec will go into discussion of which (possibly all) of these user stories need to be handled by the settings UI.
|
||||
|
||||
- Add key bindings to an action that does not already have keys assigned
|
||||
- Edit key bindings for an action
|
||||
- Remove key bindings from an action
|
||||
- Add multiple key bindings for the same action
|
||||
- Create an iterable action
|
||||
- Create a nested action
|
||||
- Choose which actions appear inside the command palette
|
||||
- See all possible actions, regardless of keys
|
||||
|
||||
Commands with properties:
|
||||
- sendInput has "input"
|
||||
- closeOtherTabs has "index"
|
||||
- closeTabsAfter has "index"
|
||||
- renameTab has "title"*
|
||||
- setTabColor has "color"*
|
||||
- newWindow has "commandline", "startingDirectory", "tabTitle", "index", "profile"
|
||||
- splitPane has "split", "commandline", "startingDirectory", "tabTitle", "index", "profile", "splitMode", "size"
|
||||
- copy has "singleLine", "copyFormatting"
|
||||
- scrollUp has "rowsToScroll"
|
||||
- scrollDown has "rowsToScroll"
|
||||
- setColorScheme has "colorScheme"
|
||||
|
||||
Majority of these commands listed above are intended for the command palette, so they wouldn't make much sense with keys assigned to them anyway.
|
||||
|
||||
### Future Considerations
|
||||
|
||||
One day we'll have actions that can be invoked by items in the dropdown menu. This setting will have to live somewhere. Also, once we get a status bar, people may want to invoke actions from there.
|
||||
|
||||
## Solution Design
|
||||
|
||||
### Proposal 1: Keyboard and Command Palette pages
|
||||
|
||||
Implement a Keyboard page in place of the Actions page. Also plan for a Command Palette page in the future if it's something that's heavily requested. The Command Palette page would cover the missing use cases listed below.
|
||||
|
||||
When users want to add a new key binding, the dropdown will list every action, regardless if it already has keys assigned. This page should show every key binding assigned to an action, even if there are multiple bindings to the same action.
|
||||
|
||||
Users will be able to view every possible action from the command palette if they'd like.
|
||||
|
||||
Use cases covered:
|
||||
- Add key bindings to an action that does not already have keys assigned
|
||||
- Edit key bindings for an action
|
||||
- Remove key bindings from an action
|
||||
- Add multiple key bindings for the same action
|
||||
- See all actions that have keys assigned
|
||||
|
||||
Use cases missing:
|
||||
- Create an iterable action
|
||||
- Create a nested action
|
||||
- Choose which actions appear inside the command palette
|
||||
- See all possible actions, regardless of keys
|
||||
|
||||
* **Pros**:
|
||||
- This allows people to edit their actions in most of their scenarios.
|
||||
- This gives us some wiggle room to cover majority of the use cases we need and seeing if people want the other use cases that are missing.
|
||||
|
||||
* **Cons**:
|
||||
- Unfortunately we couldn't cover every single use case with this design.
|
||||
- You can't edit the properties that are on some commands, however the default commands from the command palette include options with properties anyway. For example "decrease font size" has the `delta` property already included.
|
||||
|
||||
### Proposal 2: Have everything on one Actions page
|
||||
|
||||
Implement an Actions page that allows you to create actions designed for the command palette as well as actions with keys.
|
||||
|
||||
Use cases covered:
|
||||
- Add key bindings to an action that does not already have keys assigned
|
||||
- Edit key bindings for an action
|
||||
- Remove key bindings from an action
|
||||
- Add multiple key bindings for the same action
|
||||
- See all actions that have keys assigned
|
||||
- Create an iterable action
|
||||
- Create a nested action
|
||||
- Choose which actions appear inside the command palette
|
||||
- See all possible actions, regardless of keys
|
||||
|
||||
I could not come up with a UX design that wasn't too complicated or confusing for this scenario.
|
||||
|
||||
**Pros**:
|
||||
- There is full parity with the JSON file.
|
||||
|
||||
**Cons**:
|
||||
- Could not come up with a simplistic design to represent all of the use cases (which makes the settings UI not as enticing since it promotes ease of use).
|
||||
|
||||
## Conclusion
|
||||
|
||||
We considered Proposal 2, however the design became cluttered very quickly and we agreed to create two pages and start off with Proposal 1.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||

|
||||
|
||||
The Add new button is using the secondary color, to align with the button on the Color schemes page.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Potential Issues
|
||||
|
||||
This design is not 1:1 with the JSON file, so actions that don't have keys will not appear on this page. Additionally, you can't add a new action without keys with this current design.
|
||||
|
||||
You also cannot specify properties on commands (like the `newTab` command) and these will have to be added through the JSON file. Considering there are only a few of these and we're planning to iterate on this and add a Command Palette page, we were okay with this decision.
|
||||
|
||||
## Resources
|
||||
|
||||
### Footnotes
|
||||
@@ -29,8 +29,8 @@ Below is the schedule for when milestones will be included in release builds of
|
||||
| 2021-03-01 | [1.7] in Windows Terminal Preview<br>[1.6] in Windows Terminal | [Windows Terminal Preview 1.7 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-7-release/) |
|
||||
| 2021-04-14 | [1.8] in Windows Terminal Preview<br>[1.7] in Windows Terminal | [Windows Terminal Preview 1.8 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-8-release/) |
|
||||
| 2021-05-31 | [1.9] in Windows Terminal Preview<br>[1.8] in Windows Terminal | [Windows Terminal Preview 1.9 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-9-release/) |
|
||||
| 2021-07-31 | 1.10 in Windows Terminal Preview<br>[1.9] in Windows Terminal | |
|
||||
| 2021-08-30 | 1.11 in Windows Terminal Preview<br>1.10 in Windows Terminal | |
|
||||
| 2021-07-14 | [1.10] in Windows Terminal Preview<br>[1.9] in Windows Terminal | [Windows Terminal Preview 1.10 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-10-release/) |
|
||||
| 2021-08-31 | [1.11] in Windows Terminal Preview<br>[1.10] in Windows Terminal | [Windows Terminal Preview 1.11 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/) |
|
||||
| 2021-10-31 | 1.12 in Windows Terminal Preview<br>1.11 in Windows Terminal | |
|
||||
| 2021-11-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
|
||||
| 2021-12-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
|
||||
@@ -89,6 +89,8 @@ Feature Notes:
|
||||
[1.7]: https://github.com/microsoft/terminal/milestone/32
|
||||
[1.8]: https://github.com/microsoft/terminal/milestone/33
|
||||
[1.9]: https://github.com/microsoft/terminal/milestone/34
|
||||
[1.10]: https://github.com/microsoft/terminal/milestone/35
|
||||
[1.11]: https://github.com/microsoft/terminal/milestone/36
|
||||
[2.0]: https://github.com/microsoft/terminal/milestone/22
|
||||
[#1564]: https://github.com/microsoft/terminal/issues/1564
|
||||
[#6720]: https://github.com/microsoft/terminal/pull/6720
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @Austin-Lamb in December 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
@@ -12,4 +12,4 @@ That provenance file is automatically read and inventoried by Microsoft systems
|
||||
2. Take the parts you want, but leave most of it behind since it's HUGE and will bloat the repo to take it all. At the time of this writing, we only use small_vector.hpp and its dependencies as a header-only library.
|
||||
3. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in a version-specific subdirectory below this readme.
|
||||
If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage.
|
||||
4. Submit the pull.
|
||||
4. Submit the pull.
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @miniksa in January 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @miniksa in March 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @DHowett-MSFT in April 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @PankajBhojwani in September 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @miniksa in March 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,4 +4,4 @@ This manifest anchors our usage of rgb.txt from the X11 distribution.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -8,5 +8,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
|
||||
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code, Cascadia Mono (2106.17)
|
||||
* from microsoft/cascadia-code@fb0bce69c1c12f6c298b8bc1c1d181868f5daa9a
|
||||
* Cascadia Code, Cascadia Mono (2108.26)
|
||||
* from microsoft/cascadia-code@f91d08f703ee61cf4ae936b9700ca974de2748fe
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "MyPage.h"
|
||||
#include <LibraryResources.h>
|
||||
#include "MyPage.g.cpp"
|
||||
#include "..\..\..\src\cascadia\UnitTests_Control\MockControlSettings.h"
|
||||
#include "MySettings.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
@@ -26,17 +26,24 @@ namespace winrt::SampleApp::implementation
|
||||
|
||||
void MyPage::Create()
|
||||
{
|
||||
TerminalConnection::EchoConnection conn{};
|
||||
auto settings = winrt::make_self<ControlUnitTests::MockControlSettings>();
|
||||
auto settings = winrt::make_self<implementation::MySettings>();
|
||||
|
||||
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted in-proc...",
|
||||
winrt::hstring{},
|
||||
L"",
|
||||
nullptr,
|
||||
32,
|
||||
80,
|
||||
winrt::guid()) };
|
||||
|
||||
// "Microsoft.Terminal.TerminalConnection.ConptyConnection"
|
||||
winrt::hstring myClass{ winrt::name_of<TerminalConnection::ConptyConnection>() };
|
||||
TerminalConnection::ConnectionInformation connectInfo{ myClass, connectionSettings };
|
||||
|
||||
TerminalConnection::ITerminalConnection conn{ TerminalConnection::ConnectionInformation::CreateConnection(connectInfo) };
|
||||
Control::TermControl control{ *settings, conn };
|
||||
|
||||
InProcContent().Children().Append(control);
|
||||
|
||||
// Once the control loads (and not before that), write some text for debugging:
|
||||
control.Initialized([conn](auto&&, auto&&) {
|
||||
conn.WriteInput(L"This TermControl is hosted in-proc...");
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -19,10 +19,15 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Button x:Name="TabRow"
|
||||
Grid.Row="0">
|
||||
"Tabs"
|
||||
</Button>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox x:Name="GuidInput"
|
||||
Width="400"
|
||||
PlaceholderText="{}{guid here}" />
|
||||
<Button Grid.Row="0">
|
||||
Create
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<Grid x:Name="TabContent"
|
||||
Grid.Row="1"
|
||||
|
||||
@@ -4,6 +4,8 @@ Licensed under the MIT license.
|
||||
--*/
|
||||
#pragma once
|
||||
#include "../../inc/cppwinrt_utils.h"
|
||||
#include "../types/inc/colorTable.hpp"
|
||||
|
||||
#include <DefaultSettings.h>
|
||||
#include <conattrs.hpp>
|
||||
#include "MySettings.g.h"
|
||||
@@ -12,9 +14,6 @@ namespace winrt::SampleApp::implementation
|
||||
{
|
||||
struct MySettings : MySettingsT<MySettings>
|
||||
{
|
||||
public:
|
||||
MySettings() = default;
|
||||
|
||||
// --------------------------- Core Settings ---------------------------
|
||||
// All of these settings are defined in ICoreSettings.
|
||||
|
||||
@@ -88,6 +87,14 @@ namespace winrt::SampleApp::implementation
|
||||
winrt::Microsoft::Terminal::Core::Color GetColorTableEntry(int32_t index) noexcept { return _ColorTable.at(index); }
|
||||
std::array<winrt::Microsoft::Terminal::Core::Color, 16> ColorTable() { return _ColorTable; }
|
||||
void ColorTable(std::array<winrt::Microsoft::Terminal::Core::Color, 16> /*colors*/) {}
|
||||
|
||||
MySettings()
|
||||
{
|
||||
const auto campbellSpan = ::Microsoft::Console::Utils::CampbellColorTable();
|
||||
std::transform(campbellSpan.begin(), campbellSpan.end(), _ColorTable.begin(), [](auto&& color) {
|
||||
return static_cast<winrt::Microsoft::Terminal::Core::Color>(til::color{ color });
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
<Private>true</Private>
|
||||
<CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
|
||||
</ProjectReference>
|
||||
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj">
|
||||
<Project>{18D09A24-8240-42D6-8CB6-236EEE820263}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<packages>
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
|
||||
|
||||
@@ -6,7 +6,7 @@ Module Name:
|
||||
- OutputCellView.hpp
|
||||
|
||||
Abstract:
|
||||
- Read-only view into a single cell of data that someone is attempting to write into the output buffer.
|
||||
- Read view into a single cell of data that someone is attempting to write into the output buffer.
|
||||
- This is done for performance reasons (avoid heap allocs and copies).
|
||||
|
||||
Author:
|
||||
@@ -36,6 +36,21 @@ public:
|
||||
TextAttribute TextAttr() const noexcept;
|
||||
TextAttributeBehavior TextAttrBehavior() const noexcept;
|
||||
|
||||
void UpdateText(const std::wstring_view& view) noexcept
|
||||
{
|
||||
_view = view;
|
||||
};
|
||||
|
||||
void UpdateDbcsAttribute(const DbcsAttribute& dbcsAttr) noexcept
|
||||
{
|
||||
_dbcsAttr = dbcsAttr;
|
||||
}
|
||||
|
||||
void UpdateTextAttribute(const TextAttribute& textAttr) noexcept
|
||||
{
|
||||
_textAttr = textAttr;
|
||||
}
|
||||
|
||||
bool operator==(const OutputCellView& view) const noexcept;
|
||||
bool operator!=(const OutputCellView& view) const noexcept;
|
||||
|
||||
|
||||
@@ -95,16 +95,18 @@ bool TextAttribute::IsLegacy() const noexcept
|
||||
// - defaultFgColor: the default foreground color rgb value.
|
||||
// - defaultBgColor: the default background color rgb value.
|
||||
// - reverseScreenMode: true if the screen mode is reversed.
|
||||
// - blinkingIsFaint: true if blinking should be interpreted as faint.
|
||||
// - blinkingIsFaint: true if blinking should be interpreted as faint. (defaults to false)
|
||||
// - boldIsBright: true if "bold" should be interpreted as bright. (defaults to true)
|
||||
// Return Value:
|
||||
// - the foreground and background colors that should be displayed.
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode,
|
||||
const bool blinkingIsFaint) const noexcept
|
||||
const bool blinkingIsFaint,
|
||||
const bool boldIsBright) const noexcept
|
||||
{
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgColor, IsBold());
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgColor, boldIsBright && IsBold());
|
||||
auto bg = _background.GetColor(colorTable, defaultBgColor);
|
||||
if (IsFaint() || (IsBlinking() && blinkingIsFaint))
|
||||
{
|
||||
|
||||
@@ -64,11 +64,12 @@ public:
|
||||
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
|
||||
WORD GetLegacyAttributes() const noexcept;
|
||||
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode = false,
|
||||
const bool blinkingIsFaint = false) const noexcept;
|
||||
const bool blinkingIsFaint = false,
|
||||
const bool boldIsBright = true) const noexcept;
|
||||
|
||||
bool IsLeadingByte() const noexcept;
|
||||
bool IsTrailingByte() const noexcept;
|
||||
@@ -212,7 +213,7 @@ constexpr bool operator!=(const TextAttribute& a, const TextAttribute& b) noexce
|
||||
#ifdef UNIT_TESTING
|
||||
|
||||
#define LOG_ATTR(attr) (Log::Comment(NoThrowString().Format( \
|
||||
L#attr L"=%s", VerifyOutputTraits<TextAttribute>::ToString(attr).GetBuffer())))
|
||||
L## #attr L"=%s", VerifyOutputTraits<TextAttribute>::ToString(attr).GetBuffer())))
|
||||
|
||||
namespace WEX
|
||||
{
|
||||
|
||||
@@ -50,6 +50,9 @@ constexpr std::array<BYTE, 256> Index256ToIndex16 = {
|
||||
|
||||
// clang-format on
|
||||
|
||||
// We should only need 4B for TextColor. Any more than that is just waste.
|
||||
static_assert(sizeof(TextColor) == 4);
|
||||
|
||||
bool TextColor::CanBeBrightened() const noexcept
|
||||
{
|
||||
return IsIndex16() || IsDefault();
|
||||
@@ -138,15 +141,12 @@ void TextColor::SetDefault() noexcept
|
||||
// - brighten: if true, we'll brighten a dark color table index.
|
||||
// Return Value:
|
||||
// - a COLORREF containing the real value of this TextColor.
|
||||
COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
bool brighten) const noexcept
|
||||
COLORREF TextColor::GetColor(const std::array<COLORREF, 256>& colorTable, const COLORREF defaultColor, bool brighten) const noexcept
|
||||
{
|
||||
if (IsDefault())
|
||||
{
|
||||
if (brighten)
|
||||
{
|
||||
FAIL_FAST_IF(colorTable.size() < 16);
|
||||
// See MSFT:20266024 for context on this fix.
|
||||
// Additionally todo MSFT:20271956 to fix this better for 19H2+
|
||||
// If we're a default color, check to see if the defaultColor exists
|
||||
@@ -156,6 +156,61 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
||||
// (Settings::_DefaultForeground==INVALID_COLOR, and the index
|
||||
// from _wFillAttribute is being used instead.)
|
||||
// If we find a match, return instead the bright version of this color
|
||||
|
||||
static_assert(sizeof(COLORREF) * 8 == 32, "The vectorized code broke. If you can't fix COLORREF, just remove the vectorized code.");
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
#pragma warning(disable : 26490) // Don't use reinterpret_cast (type.1).
|
||||
#ifdef __AVX2__
|
||||
// I wrote this vectorized code one day, because the sun was shining so nicely.
|
||||
// There's no other reason for this to exist here, except for being pretty.
|
||||
// This code implements the exact same for loop you can find below, but is ~3x faster.
|
||||
//
|
||||
// A brief explanation for people unfamiliar with vectorized instructions:
|
||||
// Vectorized instructions, like "SSE" or "AVX", allow you to run
|
||||
// common operations like additions, multiplications, comparisons,
|
||||
// or bitwise operations concurrently on multiple values at once.
|
||||
//
|
||||
// We want to find the given defaultColor in the first 8 values of colorTable.
|
||||
// Coincidentally a COLORREF is a DWORD and 8 of them are exactly 256 bits.
|
||||
// -- The size of a single AVX register.
|
||||
//
|
||||
// Thus, the code works like this:
|
||||
// 1. Load all 8 DWORDs at once into one register
|
||||
// 2. Set the same defaultColor 8 times in another register
|
||||
// 3. Compare all 8 values at once
|
||||
// The result is either 0xffffffff or 0x00000000.
|
||||
// 4. Extract the most significant bit of each DWORD
|
||||
// Assuming that no duplicate colors exist in colorTable,
|
||||
// the result will be something like 0b00100000.
|
||||
// 5. Use BitScanForward (bsf) to find the index of the most significant 1 bit.
|
||||
const auto haystack = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(colorTable.data())); // 1.
|
||||
const auto needle = _mm256_set1_epi32(__builtin_bit_cast(int, defaultColor)); // 2.
|
||||
const auto result = _mm256_cmpeq_epi32(haystack, needle); // 3.
|
||||
const auto mask = _mm256_movemask_ps(_mm256_castsi256_ps(result)); // 4.
|
||||
unsigned long index;
|
||||
return _BitScanForward(&index, mask) ? til::at(colorTable, static_cast<size_t>(index) + 8) : defaultColor; // 5.
|
||||
#elif _M_AMD64
|
||||
// If you look closely this SSE2 algorithm is the same as the AVX one.
|
||||
// The two differences are that we need to:
|
||||
// * do everything twice, because SSE is limited to 128 bits and not 256.
|
||||
// * use _mm_packs_epi32 to merge two 128 bits vectors into one in step 3.5.
|
||||
// _mm_packs_epi32 takes two SSE registers and truncates all 8 DWORDs into 8 WORDs,
|
||||
// the latter of which fits into a single register (which is then used in the identical step 4).
|
||||
// * since the result are now 8 WORDs, we need to use _mm_movemask_epi8 (there's no 16-bit variant),
|
||||
// which unlike AVX's step 4 results in in something like 0b0000110000000000.
|
||||
// --> the index returned by _BitScanForward must be divided by 2.
|
||||
const auto haystack1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 0));
|
||||
const auto haystack2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 4));
|
||||
const auto needle = _mm_set1_epi32(__builtin_bit_cast(int, defaultColor));
|
||||
const auto result1 = _mm_cmpeq_epi32(haystack1, needle);
|
||||
const auto result2 = _mm_cmpeq_epi32(haystack2, needle);
|
||||
const auto result = _mm_packs_epi32(result1, result2); // 3.5
|
||||
const auto mask = _mm_movemask_epi8(result);
|
||||
unsigned long index;
|
||||
return _BitScanForward(&index, mask) ? til::at(colorTable, static_cast<size_t>(index / 2) + 8) : defaultColor;
|
||||
#else
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
if (til::at(colorTable, i) == defaultColor)
|
||||
@@ -163,6 +218,8 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
||||
return til::at(colorTable, i + 8);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
return defaultColor;
|
||||
@@ -199,7 +256,7 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
|
||||
}
|
||||
else if (IsIndex256())
|
||||
{
|
||||
return Index256ToIndex16.at(GetIndex());
|
||||
return til::at(Index256ToIndex16, GetIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -208,7 +265,7 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
|
||||
const BYTE compressedRgb = (_red & 0b11100000) +
|
||||
((_green >> 3) & 0b00011100) +
|
||||
((_blue >> 6) & 0b00000011);
|
||||
return CompressedRgbToIndex16.at(compressedRgb);
|
||||
return til::at(CompressedRgbToIndex16, compressedRgb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,10 +86,7 @@ public:
|
||||
void SetIndex(const BYTE index, const bool isIndex256) noexcept;
|
||||
void SetDefault() noexcept;
|
||||
|
||||
COLORREF GetColor(gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
const bool brighten = false) const noexcept;
|
||||
|
||||
COLORREF GetColor(const std::array<COLORREF, 256>& colorTable, const COLORREF defaultColor, bool brighten = false) const noexcept;
|
||||
BYTE GetLegacyIndex(const BYTE defaultIndex) const noexcept;
|
||||
|
||||
constexpr BYTE GetIndex() const noexcept
|
||||
@@ -157,5 +154,3 @@ namespace WEX
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(TextColor) <= 4 * sizeof(BYTE), "We should only need 4B for an entire TextColor. Any more than that is just waste");
|
||||
|
||||
@@ -315,6 +315,11 @@ void Cursor::StartDeferDrawing() noexcept
|
||||
_fDeferCursorRedraw = true;
|
||||
}
|
||||
|
||||
bool Cursor::IsDeferDrawing() noexcept
|
||||
{
|
||||
return _fDeferCursorRedraw;
|
||||
}
|
||||
|
||||
void Cursor::EndDeferDrawing() noexcept
|
||||
{
|
||||
if (_fHaveDeferredCursorRedraw)
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
const COLORREF GetColor() const noexcept;
|
||||
|
||||
void StartDeferDrawing() noexcept;
|
||||
bool IsDeferDrawing() noexcept;
|
||||
void EndDeferDrawing() noexcept;
|
||||
|
||||
void SetHasMoved(const bool fHasMoved) noexcept;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<ClInclude Include="..\Row.hpp" />
|
||||
<ClInclude Include="..\search.h" />
|
||||
<ClInclude Include="..\TextColor.h" />
|
||||
<ClInclude Include="..\TextAttribute.h" />
|
||||
<ClInclude Include="..\TextAttribute.hpp" />
|
||||
<ClInclude Include="..\textBuffer.hpp" />
|
||||
<ClInclude Include="..\textBufferCellIterator.hpp" />
|
||||
<ClInclude Include="..\textBufferTextIterator.hpp" />
|
||||
|
||||
@@ -418,7 +418,6 @@ bool TextBuffer::InsertCharacter(const std::wstring_view chars,
|
||||
|
||||
// Store character and double byte data
|
||||
CharRow& charRow = Row.GetCharRow();
|
||||
short const cBufferWidth = GetSize().Width();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1650,13 +1649,14 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF,
|
||||
|
||||
if (!cell.DbcsAttr().IsTrailing())
|
||||
{
|
||||
selectionText.append(cell.Chars());
|
||||
const auto chars = cell.Chars();
|
||||
selectionText.append(chars);
|
||||
|
||||
if (copyTextColor)
|
||||
{
|
||||
const auto cellData = cell.TextAttr();
|
||||
const auto [CellFgAttr, CellBkAttr] = GetAttributeColors(cellData);
|
||||
for (const wchar_t wch : cell.Chars())
|
||||
for (size_t j = 0; j < chars.size(); ++j)
|
||||
{
|
||||
selectionFgAttr.push_back(CellFgAttr);
|
||||
selectionBkAttr.push_back(CellBkAttr);
|
||||
|
||||
@@ -94,20 +94,93 @@ bool TextBufferCellIterator::operator!=(const TextBufferCellIterator& it) const
|
||||
// - Reference to self after movement.
|
||||
TextBufferCellIterator& TextBufferCellIterator::operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
// Note that this method is called intensively when the terminal is under heavy load.
|
||||
// We need to aggressively optimize it, comparing to the -= operator.
|
||||
ptrdiff_t move = movement;
|
||||
auto newPos = _pos;
|
||||
while (move > 0 && !_exceeded)
|
||||
if (move < 0)
|
||||
{
|
||||
_exceeded = !_bounds.IncrementInBounds(newPos);
|
||||
// Early branching to leave the rare case to -= operator.
|
||||
// This helps reducing the instruction count within this method, which is good for instruction cache.
|
||||
return *this -= -move;
|
||||
}
|
||||
|
||||
// The remaining code in this function is functionally equivalent to:
|
||||
// auto newPos = _pos;
|
||||
// while (move > 0 && !_exceeded)
|
||||
// {
|
||||
// _exceeded = !_bounds.IncrementInBounds(newPos);
|
||||
// move--;
|
||||
// }
|
||||
// _SetPos(newPos);
|
||||
//
|
||||
// _SetPos() necessitates calling _GenerateView() and thus the construction
|
||||
// of a new OutputCellView(). This has a high performance impact (ICache spill?).
|
||||
// The code below inlines _bounds.IncrementInBounds as well as SetPos.
|
||||
// In the hot path (_pos.Y doesn't change) we modify the _view directly.
|
||||
|
||||
// Hoist these integers which will be used frequently later.
|
||||
const auto boundsRightInclusive = _bounds.RightInclusive();
|
||||
const auto boundsLeft = _bounds.Left();
|
||||
const auto boundsBottomInclusive = _bounds.BottomInclusive();
|
||||
const auto boundsTop = _bounds.Top();
|
||||
const auto oldX = _pos.X;
|
||||
const auto oldY = _pos.Y;
|
||||
|
||||
// Under MSVC writing the individual members of a COORD generates worse assembly
|
||||
// compared to having them be local variables. This causes a performance impact.
|
||||
auto newX = oldX;
|
||||
auto newY = oldY;
|
||||
|
||||
while (move > 0)
|
||||
{
|
||||
if (newX == boundsRightInclusive)
|
||||
{
|
||||
newX = boundsLeft;
|
||||
newY++;
|
||||
if (newY > boundsBottomInclusive)
|
||||
{
|
||||
newY = boundsTop;
|
||||
_exceeded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newX++;
|
||||
_exceeded = false;
|
||||
}
|
||||
move--;
|
||||
}
|
||||
while (move < 0 && !_exceeded)
|
||||
|
||||
if (_exceeded)
|
||||
{
|
||||
_exceeded = !_bounds.DecrementInBounds(newPos);
|
||||
move++;
|
||||
// Early return because nothing needs to be done here.
|
||||
return *this;
|
||||
}
|
||||
_SetPos(newPos);
|
||||
return (*this);
|
||||
|
||||
if (newY == oldY)
|
||||
{
|
||||
// hot path
|
||||
const auto diff = gsl::narrow_cast<ptrdiff_t>(newX) - gsl::narrow_cast<ptrdiff_t>(oldX);
|
||||
_attrIter += diff;
|
||||
_view.UpdateTextAttribute(*_attrIter);
|
||||
|
||||
const CharRow& charRow = _pRow->GetCharRow();
|
||||
_view.UpdateText(charRow.GlyphAt(newX));
|
||||
_view.UpdateDbcsAttribute(charRow.DbcsAttrAt(newX));
|
||||
_pos.X = newX;
|
||||
}
|
||||
else
|
||||
{
|
||||
// cold path (_GenerateView is slow)
|
||||
_pRow = s_GetRow(_buffer, { newX, newY });
|
||||
_attrIter = _pRow->GetAttrRow().cbegin() + newX;
|
||||
_pos.X = newX;
|
||||
_pos.Y = newY;
|
||||
_GenerateView();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -118,7 +191,22 @@ TextBufferCellIterator& TextBufferCellIterator::operator+=(const ptrdiff_t& move
|
||||
// - Reference to self after movement.
|
||||
TextBufferCellIterator& TextBufferCellIterator::operator-=(const ptrdiff_t& movement)
|
||||
{
|
||||
return this->operator+=(-movement);
|
||||
ptrdiff_t move = movement;
|
||||
if (move < 0)
|
||||
{
|
||||
return (*this) += (-move);
|
||||
}
|
||||
|
||||
auto newPos = _pos;
|
||||
while (move > 0 && !_exceeded)
|
||||
{
|
||||
_exceeded = !_bounds.DecrementInBounds(newPos);
|
||||
move--;
|
||||
}
|
||||
_SetPos(newPos);
|
||||
|
||||
_GenerateView();
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -22,12 +22,11 @@ class TextAttributeTests
|
||||
TEST_METHOD(TestTextAttributeColorGetters);
|
||||
TEST_METHOD(TestReverseDefaultColors);
|
||||
TEST_METHOD(TestRoundtripDefaultColors);
|
||||
TEST_METHOD(TestBoldAsBright);
|
||||
|
||||
static const int COLOR_TABLE_SIZE = 16;
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
std::array<COLORREF, 256> _colorTable;
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextAttributeTests::ClassSetup()
|
||||
@@ -51,11 +50,6 @@ bool TextAttributeTests::ClassSetup()
|
||||
return true;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> TextAttributeTests::_GetTableView()
|
||||
{
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestRoundtripLegacy()
|
||||
{
|
||||
WORD expectedLegacy = FOREGROUND_BLUE | BACKGROUND_RED;
|
||||
@@ -133,23 +127,22 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
||||
const COLORREF faintRed = RGB(127, 0, 0);
|
||||
const COLORREF green = RGB(0, 255, 0);
|
||||
TextAttribute attr(red, green);
|
||||
auto view = _GetTableView();
|
||||
|
||||
// verify that calculated foreground/background are the same as the direct
|
||||
// values when reverse video is not set
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, red), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// reset the reverse video
|
||||
attr.SetReverseVideo(false);
|
||||
@@ -158,17 +151,17 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
||||
// while the background and getters stay the same
|
||||
attr.SetFaint(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(faintRed, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(faintRed, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched, and the background fainter, while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, faintRed), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, faintRed), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// reset the reverse video and faint attributes
|
||||
attr.SetReverseVideo(false);
|
||||
@@ -178,17 +171,17 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
||||
// background, while getters stay the same
|
||||
attr.SetInvisible(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, the calculated background value should match
|
||||
// the foreground, while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, red), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestReverseDefaultColors()
|
||||
@@ -196,40 +189,39 @@ void TextAttributeTests::TestReverseDefaultColors()
|
||||
const COLORREF red = RGB(255, 0, 0);
|
||||
const COLORREF green = RGB(0, 255, 0);
|
||||
TextAttribute attr{};
|
||||
auto view = _GetTableView();
|
||||
|
||||
// verify that calculated foreground/background are the same as the direct
|
||||
// values when reverse video is not set
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
VERIFY_IS_TRUE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, _defaultFg), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, _defaultFg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
attr.SetForeground(red);
|
||||
VERIFY_IS_TRUE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, red), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
attr.Invert();
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
attr.SetDefaultForeground();
|
||||
attr.SetBackground(green);
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestRoundtripDefaultColors()
|
||||
@@ -272,3 +264,56 @@ void TextAttributeTests::TestRoundtripDefaultColors()
|
||||
// Reset the legacy default colors to white on black.
|
||||
TextAttribute::SetLegacyDefaultAttributes(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestBoldAsBright()
|
||||
{
|
||||
const COLORREF darkBlack = til::at(_colorTable, 0);
|
||||
const COLORREF brightBlack = til::at(_colorTable, 8);
|
||||
const COLORREF darkGreen = til::at(_colorTable, 2);
|
||||
|
||||
TextAttribute attr{};
|
||||
|
||||
// verify that calculated foreground/background are the same as the direct
|
||||
// values when not bold
|
||||
VERIFY_IS_FALSE(attr.IsBold());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
// with bold set, calculated foreground/background values shouldn't change for the default colors.
|
||||
attr.SetBold(true);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
attr.SetIndexedForeground(0);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
|
||||
Log::Comment(L"Foreground should be bright black when bold is bright is enabled");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
|
||||
Log::Comment(L"Foreground should be dark black when bold is bright is disabled");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
attr.SetIndexedBackground(2);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
|
||||
Log::Comment(L"background should be unaffected by 'bold is bright'");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
attr.SetBold(false);
|
||||
VERIFY_IS_FALSE(attr.IsBold());
|
||||
Log::Comment(L"when not bold, 'bold is bright' changes nothing");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
Log::Comment(L"When set to a bright color, and bold, 'bold is bright' changes nothing");
|
||||
attr.SetBold(true);
|
||||
attr.SetIndexedForeground(8);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
}
|
||||
|
||||
@@ -23,11 +23,9 @@ class TextColorTests
|
||||
TEST_METHOD(TestRgbColor);
|
||||
TEST_METHOD(TestChangeColor);
|
||||
|
||||
static const int COLOR_TABLE_SIZE = 16;
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
std::array<COLORREF, 256> _colorTable;
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextColorTests::ClassSetup()
|
||||
@@ -51,11 +49,6 @@ bool TextColorTests::ClassSetup()
|
||||
return true;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> TextColorTests::_GetTableView()
|
||||
{
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextColorTests::TestDefaultColor()
|
||||
{
|
||||
TextColor defaultColor;
|
||||
@@ -64,18 +57,16 @@ void TextColorTests::TestDefaultColor()
|
||||
VERIFY_IS_FALSE(defaultColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(defaultColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = defaultColor.GetColor(view, _defaultFg, false);
|
||||
auto color = defaultColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = defaultColor.GetColor(view, _defaultFg, true);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = defaultColor.GetColor(view, _defaultBg, false);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
color = defaultColor.GetColor(view, _defaultBg, true);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
}
|
||||
|
||||
@@ -87,18 +78,16 @@ void TextColorTests::TestDarkIndexColor()
|
||||
VERIFY_IS_TRUE(indexColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(indexColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = indexColor.GetColor(view, _defaultFg, false);
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultFg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, false);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
||||
@@ -110,18 +99,16 @@ void TextColorTests::TestBrightIndexColor()
|
||||
VERIFY_IS_TRUE(indexColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(indexColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = indexColor.GetColor(view, _defaultFg, false);
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultFg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, false);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
||||
@@ -134,18 +121,16 @@ void TextColorTests::TestRgbColor()
|
||||
VERIFY_IS_FALSE(rgbColor.IsLegacy());
|
||||
VERIFY_IS_TRUE(rgbColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
}
|
||||
|
||||
@@ -158,57 +143,55 @@ void TextColorTests::TestChangeColor()
|
||||
VERIFY_IS_FALSE(rgbColor.IsLegacy());
|
||||
VERIFY_IS_TRUE(rgbColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
rgbColor.SetDefault();
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
rgbColor.SetIndex(7, false);
|
||||
color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
rgbColor.SetIndex(15, false);
|
||||
color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
||||
@@ -397,6 +397,10 @@ namespace SettingsModelLocalTests
|
||||
"name":"action6",
|
||||
"command": { "action": "newWindow", "startingDirectory":"C:\\foo", "commandline": "bar.exe" }
|
||||
},
|
||||
{
|
||||
"name":"action7_startingDirectoryWithTrailingSlash",
|
||||
"command": { "action": "newWindow", "startingDirectory":"C:\\", "commandline": "bar.exe" }
|
||||
},
|
||||
])" };
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
@@ -405,7 +409,7 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
VERIFY_ARE_EQUAL(7u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(8u, commands.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action0");
|
||||
@@ -503,5 +507,20 @@ namespace SettingsModelLocalTests
|
||||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\foo\" -- \"bar.exe\"", terminalArgs.ToCommandline());
|
||||
}
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action7_startingDirectoryWithTrailingSlash");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
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);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\\\\" -- \"bar.exe\"", terminalArgs.ToCommandline());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1971,9 +1971,9 @@ namespace SettingsModelLocalTests
|
||||
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, settings->_globals->_actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('a') }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('b') }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('A'), 0 }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('B'), 0 }));
|
||||
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
for (const auto& warning : settings->_globals->_keybindingsWarnings)
|
||||
{
|
||||
@@ -2124,7 +2124,7 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('A'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -2141,7 +2141,7 @@ namespace SettingsModelLocalTests
|
||||
Log::Comment(L"Note that we're skipping ctrl+B, since that doesn't have `keys` set.");
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -2155,7 +2155,7 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('D'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -2169,7 +2169,7 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('E'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -2183,7 +2183,7 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -2841,7 +2841,7 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
|
||||
VERIFY_ARE_EQUAL(1u, nameMap.Size());
|
||||
|
||||
const KeyChord expectedKeyChord{ true, false, true, static_cast<int>('W') };
|
||||
const KeyChord expectedKeyChord{ true, false, true, false, static_cast<int>('W'), 0 };
|
||||
{
|
||||
// Verify NameMap returns correct value
|
||||
const auto& cmd{ nameMap.TryLookup(L"foo") };
|
||||
|
||||
@@ -36,10 +36,15 @@ namespace SettingsModelLocalTests
|
||||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(KeyChords);
|
||||
TEST_METHOD(ManyKeysSameAction);
|
||||
TEST_METHOD(LayerKeybindings);
|
||||
TEST_METHOD(UnbindKeybindings);
|
||||
|
||||
TEST_METHOD(LayerScancodeKeybindings);
|
||||
|
||||
TEST_METHOD(TestExplicitUnbind);
|
||||
|
||||
TEST_METHOD(TestArbitraryArgs);
|
||||
TEST_METHOD(TestSplitPaneArgs);
|
||||
|
||||
@@ -53,6 +58,7 @@ namespace SettingsModelLocalTests
|
||||
TEST_METHOD(TestMoveTabArgs);
|
||||
|
||||
TEST_METHOD(TestGetKeyBindingForAction);
|
||||
TEST_METHOD(KeybindingsWithoutVkey);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
@@ -61,6 +67,69 @@ namespace SettingsModelLocalTests
|
||||
}
|
||||
};
|
||||
|
||||
void KeyBindingsTests::KeyChords()
|
||||
{
|
||||
struct testCase
|
||||
{
|
||||
VirtualKeyModifiers modifiers;
|
||||
int32_t vkey;
|
||||
int32_t scanCode;
|
||||
std::wstring_view expected;
|
||||
};
|
||||
|
||||
static constexpr std::array testCases{
|
||||
testCase{
|
||||
VirtualKeyModifiers::None,
|
||||
'A',
|
||||
0,
|
||||
L"a",
|
||||
},
|
||||
testCase{
|
||||
VirtualKeyModifiers::Control,
|
||||
'A',
|
||||
0,
|
||||
L"ctrl+a",
|
||||
},
|
||||
testCase{
|
||||
VirtualKeyModifiers::Control | VirtualKeyModifiers::Shift,
|
||||
VK_OEM_PLUS,
|
||||
0,
|
||||
L"ctrl+shift+plus",
|
||||
},
|
||||
testCase{
|
||||
VirtualKeyModifiers::Control | VirtualKeyModifiers::Menu | VirtualKeyModifiers::Shift | VirtualKeyModifiers::Windows,
|
||||
255,
|
||||
0,
|
||||
L"win+ctrl+alt+shift+vk(255)",
|
||||
},
|
||||
testCase{
|
||||
VirtualKeyModifiers::Control | VirtualKeyModifiers::Menu | VirtualKeyModifiers::Shift | VirtualKeyModifiers::Windows,
|
||||
0,
|
||||
123,
|
||||
L"win+ctrl+alt+shift+sc(123)",
|
||||
},
|
||||
};
|
||||
|
||||
for (const auto& tc : testCases)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"Testing case:\"%s\"", tc.expected.data()));
|
||||
|
||||
const auto actualString = KeyChordSerialization::ToString({ tc.modifiers, tc.vkey, tc.scanCode });
|
||||
VERIFY_ARE_EQUAL(tc.expected, actualString);
|
||||
|
||||
auto expectedVkey = tc.vkey;
|
||||
if (!expectedVkey)
|
||||
{
|
||||
expectedVkey = MapVirtualKeyW(tc.scanCode, MAPVK_VSC_TO_VK_EX);
|
||||
}
|
||||
|
||||
const auto actualKeyChord = KeyChordSerialization::FromString(actualString);
|
||||
VERIFY_ARE_EQUAL(tc.modifiers, actualKeyChord.Modifiers());
|
||||
VERIFY_ARE_EQUAL(expectedVkey, actualKeyChord.Vkey());
|
||||
VERIFY_ARE_EQUAL(tc.scanCode, actualKeyChord.ScanCode());
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingsTests::ManyKeysSameAction()
|
||||
{
|
||||
const std::string bindings0String{ R"([ { "command": "copy", "keys": ["ctrl+c"] } ])" };
|
||||
@@ -75,7 +144,6 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
@@ -99,7 +167,6 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
@@ -129,7 +196,6 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings5Json = VerifyParseSucceeded(bindings5String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
@@ -142,7 +208,7 @@ namespace SettingsModelLocalTests
|
||||
L"Try unbinding a key using `\"unbound\"` to unbind the key"));
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using `null` to unbind the key"));
|
||||
@@ -152,7 +218,7 @@ namespace SettingsModelLocalTests
|
||||
// Then try layering in the bad setting
|
||||
actionMap->LayerJson(bindings3Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using an unrecognized command to unbind the key"));
|
||||
@@ -162,7 +228,7 @@ namespace SettingsModelLocalTests
|
||||
// Then try layering in the bad setting
|
||||
actionMap->LayerJson(bindings4Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using a straight up invalid value to unbind the key"));
|
||||
@@ -172,13 +238,38 @@ namespace SettingsModelLocalTests
|
||||
// Then try layering in the bad setting
|
||||
actionMap->LayerJson(bindings5Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key that wasn't bound at all"));
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('c') }));
|
||||
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestExplicitUnbind()
|
||||
{
|
||||
const std::string bindings0String{ R"([ { "command": "copy", "keys": ["ctrl+c"] } ])" };
|
||||
const std::string bindings1String{ R"([ { "command": "unbound", "keys": ["ctrl+c"] } ])" };
|
||||
const std::string bindings2String{ R"([ { "command": "copy", "keys": ["ctrl+c"] } ])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
const KeyChord keyChord{ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 };
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
|
||||
actionMap->LayerJson(bindings1Json);
|
||||
VERIFY_IS_TRUE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestArbitraryArgs()
|
||||
@@ -203,7 +294,6 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
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());
|
||||
@@ -211,10 +301,9 @@ namespace SettingsModelLocalTests
|
||||
{
|
||||
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') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
@@ -222,10 +311,9 @@ 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') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
@@ -233,10 +321,9 @@ 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') };
|
||||
KeyChord kc{ false, true, true, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.SingleLine());
|
||||
}
|
||||
@@ -244,11 +331,10 @@ 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') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('T'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<NewTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
@@ -256,11 +342,10 @@ namespace SettingsModelLocalTests
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` parses args correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('T') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('T'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<NewTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
@@ -270,11 +355,10 @@ namespace SettingsModelLocalTests
|
||||
Log::Comment(NoThrowString().Format(
|
||||
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') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('Y'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<NewTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
@@ -284,11 +368,10 @@ 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') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('B'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
@@ -296,11 +379,10 @@ 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') };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>('B'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
@@ -308,11 +390,10 @@ 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') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<AdjustFontSizeArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(1, realArgs.Delta());
|
||||
}
|
||||
@@ -320,11 +401,10 @@ 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') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('G'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<AdjustFontSizeArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(-1, realArgs.Delta());
|
||||
}
|
||||
@@ -342,44 +422,39 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
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') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('D'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('E'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('G'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('H'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
@@ -396,37 +471,33 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
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') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SetTabColorArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.TabColor());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('D'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SetTabColorArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TabColor());
|
||||
// Remember that COLORREFs are actually BBGGRR order, while the string is in #RRGGBB order
|
||||
VERIFY_ARE_EQUAL(til::color(0x563412), til::color(realArgs.TabColor().Value()));
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<SetTabColorArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.TabColor());
|
||||
}
|
||||
@@ -441,16 +512,14 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
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') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& realArgs = actionAndArgs.Args().as<CopyTextArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
@@ -470,63 +539,56 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
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) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollUpArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.RowsToScroll());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_DOWN) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_DOWN), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollDownArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.RowsToScroll());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollUpArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.RowsToScroll());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_DOWN) };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>(VK_DOWN), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollDownArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.RowsToScroll());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollUpArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.RowsToScroll());
|
||||
VERIFY_ARE_EQUAL(10u, realArgs.RowsToScroll().Value());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_DOWN) };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>(VK_DOWN), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ScrollDownArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.RowsToScroll());
|
||||
VERIFY_ARE_EQUAL(10u, realArgs.RowsToScroll().Value());
|
||||
@@ -535,7 +597,6 @@ namespace SettingsModelLocalTests
|
||||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "scrollDown", "rowsToScroll": -1 } }])" };
|
||||
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
|
||||
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);
|
||||
}
|
||||
@@ -551,26 +612,23 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
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) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<MoveTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.Direction(), MoveTabDirection::Forward);
|
||||
}
|
||||
{
|
||||
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_DOWN) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_DOWN), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<MoveTabArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.Direction(), MoveTabDirection::Backward);
|
||||
}
|
||||
@@ -584,7 +642,6 @@ namespace SettingsModelLocalTests
|
||||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "moveTab", "direction": "bad" } }])" };
|
||||
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
|
||||
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);
|
||||
}
|
||||
@@ -601,35 +658,31 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
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) };
|
||||
KeyChord kc{ false, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ToggleCommandPaletteArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.LaunchMode(), CommandPaletteLaunchMode::Action);
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ToggleCommandPaletteArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.LaunchMode(), CommandPaletteLaunchMode::Action);
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_UP) };
|
||||
KeyChord kc{ true, false, true, false, static_cast<int32_t>(VK_UP), 0 };
|
||||
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);
|
||||
const auto& realArgs = actionAndArgs.Args().as<ToggleCommandPaletteArgs>();
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(realArgs.LaunchMode(), CommandPaletteLaunchMode::CommandLine);
|
||||
}
|
||||
@@ -637,7 +690,6 @@ namespace SettingsModelLocalTests
|
||||
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "commandPalette", "launchMode": "bad" } }])" };
|
||||
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
|
||||
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);
|
||||
}
|
||||
@@ -669,7 +721,6 @@ namespace SettingsModelLocalTests
|
||||
};
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_IS_NOT_NULL(actionMap);
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
{
|
||||
@@ -677,7 +728,7 @@ namespace SettingsModelLocalTests
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CloseWindow) };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('A') }, kbd);
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('A'), 0 }, kbd);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"command with args");
|
||||
@@ -688,7 +739,7 @@ namespace SettingsModelLocalTests
|
||||
args->SingleLine(true);
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CopyText, *args) };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('B') }, kbd);
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('B'), 0 }, kbd);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"command with new terminal args");
|
||||
@@ -700,7 +751,7 @@ namespace SettingsModelLocalTests
|
||||
auto args{ winrt::make_self<implementation::NewTabArgs>(*newTerminalArgs) };
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::NewTab, *args) };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('C') }, kbd);
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }, kbd);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"command with hidden args");
|
||||
@@ -708,7 +759,45 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size());
|
||||
|
||||
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control | VirtualKeyModifiers::Shift, static_cast<int32_t>('P') }, kbd);
|
||||
VerifyKeyChordEquality({ VirtualKeyModifiers::Control | VirtualKeyModifiers::Shift, static_cast<int32_t>('P'), 0 }, kbd);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingsTests::LayerScancodeKeybindings()
|
||||
{
|
||||
Log::Comment(L"Layering a keybinding with a character literal on top of"
|
||||
L" an equivalent sc() key should replace it.");
|
||||
|
||||
// Wrap the first one in `R"!(...)!"` because it has `()` internally.
|
||||
const std::string bindings0String{ R"!([ { "command": "quakeMode", "keys":"win+sc(41)" } ])!" };
|
||||
const std::string bindings1String{ R"([ { "keys": "win+`", "command": { "action": "globalSummon", "monitor": "any" } } ])" };
|
||||
const std::string bindings2String{ R"([ { "keys": "ctrl+shift+`", "command": { "action": "quakeMode" } } ])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
|
||||
|
||||
actionMap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size(), L"Layering the second action should replace the first one.");
|
||||
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::KeybindingsWithoutVkey()
|
||||
{
|
||||
const auto json = VerifyParseSucceeded(R"!([{"command": "quakeMode", "keys":"shift+sc(255)"}])!");
|
||||
|
||||
const auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
actionMap->LayerJson(json);
|
||||
|
||||
const auto action = actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Shift, 0, 255 });
|
||||
VERIFY_IS_NOT_NULL(action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,11 +37,13 @@ namespace SettingsModelLocalTests
|
||||
|
||||
TEST_METHOD(TestTerminalArgsForBinding);
|
||||
|
||||
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
|
||||
TEST_METHOD(MakeSettingsForProfile);
|
||||
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
|
||||
|
||||
TEST_METHOD(TestLayerProfileOnColorScheme);
|
||||
|
||||
TEST_METHOD(TestCommandlineToTitlePromotion);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
return true;
|
||||
@@ -63,7 +65,7 @@ namespace SettingsModelLocalTests
|
||||
const std::string settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
"profiles": { "list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
@@ -82,6 +84,9 @@ namespace SettingsModelLocalTests
|
||||
"commandline": "wsl.exe"
|
||||
}
|
||||
],
|
||||
"defaults": {
|
||||
"historySize": 29
|
||||
} },
|
||||
"keybindings": [
|
||||
{ "keys": ["ctrl+a"], "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": ["ctrl+b"], "command": { "action": "splitPane", "split": "vertical", "profile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" } },
|
||||
@@ -113,7 +118,7 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(12u, actionMapImpl->_KeyMap.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('A'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -126,15 +131,15 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('B') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('B'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -148,15 +153,15 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('C'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -170,15 +175,15 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('D'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -192,15 +197,15 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('E'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -214,15 +219,24 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
if constexpr (Feature_ShowProfileDefaultsInSettings::IsEnabled())
|
||||
{
|
||||
// This action specified a command but no profile; it gets reassigned to the base profile
|
||||
VERIFY_ARE_EQUAL(settings.ProfileDefaults(), profile);
|
||||
VERIFY_ARE_EQUAL(29, termSettings.HistorySize());
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
@@ -237,15 +251,15 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('G'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
@@ -257,15 +271,15 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('H'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
@@ -278,16 +292,16 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('I') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('I'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
@@ -301,16 +315,16 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('J') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('J'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
@@ -323,16 +337,16 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('K') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('K'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
@@ -346,16 +360,16 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('L') };
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('L'), 0 };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
@@ -371,10 +385,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
@@ -382,9 +396,9 @@ namespace SettingsModelLocalTests
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalSettingsTests::MakeSettingsForProfileThatDoesntExist()
|
||||
void TerminalSettingsTests::MakeSettingsForProfile()
|
||||
{
|
||||
// Test that making settings throws when the GUID doesn't exist
|
||||
// Test that making settings generally works.
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
@@ -405,32 +419,32 @@ namespace SettingsModelLocalTests
|
||||
|
||||
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
const auto profile1 = settings.FindProfile(guid1);
|
||||
const auto profile2 = settings.FindProfile(guid2);
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid1, nullptr) };
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile1, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfile should succeed");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid2, nullptr) };
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile2, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfile should succeed");
|
||||
}
|
||||
|
||||
VERIFY_THROWS(auto terminalSettings = TerminalSettings::CreateWithProfileByID(settings, guid3, nullptr), wil::ResultException, L"This call to constructor should fail");
|
||||
|
||||
try
|
||||
{
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
|
||||
@@ -554,4 +568,85 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x45, 0x67, 0x89), terminalSettings4->CursorColor()); // from profile (no color scheme)
|
||||
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings5->CursorColor()); // default
|
||||
}
|
||||
|
||||
void TerminalSettingsTests::TestCommandlineToTitlePromotion()
|
||||
{
|
||||
const std::string settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": { "list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1,
|
||||
"commandline": "cmd.exe"
|
||||
},
|
||||
],
|
||||
"defaults": {
|
||||
"historySize": 29
|
||||
} }
|
||||
})" };
|
||||
|
||||
const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
|
||||
{ // just a profile (profile wins)
|
||||
NewTerminalArgs args{};
|
||||
args.Profile(L"profile0");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // profile and command line -> no promotion (profile wins)
|
||||
NewTerminalArgs args{};
|
||||
args.Profile(L"profile0");
|
||||
args.Commandline(L"foo.exe");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // just a title -> it is propagated
|
||||
NewTerminalArgs args{};
|
||||
args.TabTitle(L"Analog Kid");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"Analog Kid", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // title and command line -> no promotion
|
||||
NewTerminalArgs args{};
|
||||
args.TabTitle(L"Digital Man");
|
||||
args.Commandline(L"foo.exe");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"Digital Man", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // just a commandline -> promotion
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"foo.exe");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
// various typesof commandline follow
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"foo.exe bar");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"\"foo exe.exe\" bar");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"foo exe.exe", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"\"\" grand designs");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L" imagine a man");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ Author(s):
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#include "til.h"
|
||||
|
||||
#include <til/mutex.h>
|
||||
#include <til/throttled_func.h>
|
||||
|
||||
// Common includes for most tests:
|
||||
#include "../../inc/argb.h"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(ParseComboCommandlineIntoArgs);
|
||||
TEST_METHOD(ParseFocusTabArgs);
|
||||
TEST_METHOD(ParseMoveFocusArgs);
|
||||
TEST_METHOD(ParseSwapPaneArgs);
|
||||
TEST_METHOD(ParseArgumentsWithParsingTerminators);
|
||||
TEST_METHOD(ParseFocusPaneArgs);
|
||||
|
||||
@@ -1207,6 +1208,119 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::ParseSwapPaneArgs()
|
||||
{
|
||||
const wchar_t* subcommand = L"swap-pane";
|
||||
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand };
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Just the subcommand, without a direction, should fail."));
|
||||
|
||||
_buildCommandlinesExpectFailureHelper(appArgs, 1u, rawCommands);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"left" };
|
||||
_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::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Left, myArgs.Direction());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"right" };
|
||||
_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::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Right, myArgs.Direction());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"up" };
|
||||
_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::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Up, myArgs.Direction());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"down" };
|
||||
_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::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Down, myArgs.Direction());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"badDirection" };
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"move-pane with an invalid direction should fail."));
|
||||
_buildCommandlinesExpectFailureHelper(appArgs, 1u, rawCommands);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"left", L";", subcommand, L"right" };
|
||||
_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::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Left, myArgs.Direction());
|
||||
|
||||
actionAndArgs = appArgs._startupActions.at(2);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Right, myArgs.Direction());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::ParseFocusPaneArgs()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
|
||||
@@ -82,6 +82,8 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(MoveFocusFromZoomedPane);
|
||||
TEST_METHOD(CloseZoomedPane);
|
||||
|
||||
TEST_METHOD(SwapPanes);
|
||||
|
||||
TEST_METHOD(NextMRUTab);
|
||||
TEST_METHOD(VerifyCommandPaletteTabSwitcherOrder);
|
||||
|
||||
@@ -93,6 +95,8 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(TestPreviewDismissScheme);
|
||||
TEST_METHOD(TestPreviewSchemeWhilePreviewing);
|
||||
|
||||
TEST_METHOD(TestClampSwitchToTab);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
return true;
|
||||
@@ -426,7 +430,7 @@ namespace TerminalAppLocalTests
|
||||
Log::Comment(L"Duplicate the tab, and don't crash");
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_DuplicateFocusedTab();
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
VERIFY_ARE_EQUAL(3u, page->_tabs.Size(), L"We should successfully duplicate a tab hosting a deleted profile.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
@@ -526,9 +530,9 @@ namespace TerminalAppLocalTests
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(2,
|
||||
VERIFY_ARE_EQUAL(3,
|
||||
tab->GetLeafPaneCount(),
|
||||
L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
L"We should successfully duplicate a pane hosting a deleted profile.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
|
||||
@@ -817,6 +821,212 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
void TabTests::SwapPanes()
|
||||
{
|
||||
auto page = _commonSetup();
|
||||
|
||||
Log::Comment(L"Setup 4 panes.");
|
||||
// Create the following layout
|
||||
// -------------------
|
||||
// | 1 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 3 | 4 |
|
||||
// | | |
|
||||
// -------------------
|
||||
uint32_t firstId = 0, secondId = 0, thirdId = 0, fourthId = 0;
|
||||
TestOnUIThread([&]() {
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
firstId = tab->_activePane->Id().value();
|
||||
// We start with 1 tab, split vertically to get
|
||||
// -------------------
|
||||
// | 1 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
page->_SplitPane(SplitState::Vertical, SplitType::Duplicate, 0.5f, nullptr);
|
||||
secondId = tab->_activePane->Id().value();
|
||||
});
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
// After this the `2` pane is focused, go back to `1` being focused
|
||||
page->_MoveFocus(FocusDirection::Left);
|
||||
});
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
// Split again to make the 3rd tab
|
||||
// -------------------
|
||||
// | 1 | |
|
||||
// | | |
|
||||
// ---------| 2 |
|
||||
// | 3 | |
|
||||
// | | |
|
||||
// -------------------
|
||||
page->_SplitPane(SplitState::Horizontal, SplitType::Duplicate, 0.5f, nullptr);
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
// Split again to make the 3rd tab
|
||||
thirdId = tab->_activePane->Id().value();
|
||||
});
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
// After this the `3` pane is focused, go back to `2` being focused
|
||||
page->_MoveFocus(FocusDirection::Right);
|
||||
});
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
// Split to create the final pane
|
||||
// -------------------
|
||||
// | 1 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 3 | 4 |
|
||||
// | | |
|
||||
// -------------------
|
||||
page->_SplitPane(SplitState::Horizontal, SplitType::Duplicate, 0.5f, nullptr);
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
fourthId = tab->_activePane->Id().value();
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// just to be complete, make sure we actually have 4 different ids
|
||||
VERIFY_ARE_NOT_EQUAL(firstId, fourthId);
|
||||
VERIFY_ARE_NOT_EQUAL(secondId, fourthId);
|
||||
VERIFY_ARE_NOT_EQUAL(thirdId, fourthId);
|
||||
VERIFY_ARE_NOT_EQUAL(firstId, thirdId);
|
||||
VERIFY_ARE_NOT_EQUAL(secondId, thirdId);
|
||||
VERIFY_ARE_NOT_EQUAL(firstId, secondId);
|
||||
});
|
||||
|
||||
// Gratuitous use of sleep to make sure that the UI has updated properly
|
||||
// after each operation.
|
||||
Sleep(250);
|
||||
// Now try to move the pane through the tree
|
||||
Log::Comment(L"Move pane to the left. This should swap panes 3 and 4");
|
||||
// -------------------
|
||||
// | 1 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 4 | 3 |
|
||||
// | | |
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
SwapPaneArgs args{ FocusDirection::Left };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// Our currently focused pane should be `4`
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value());
|
||||
|
||||
// Inspect the tree to make sure we swapped
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_rootPane->_firstChild->_secondChild->Id().value());
|
||||
VERIFY_ARE_EQUAL(thirdId, tab->_rootPane->_secondChild->_secondChild->Id().value());
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
Log::Comment(L"Move pane to up. This should swap panes 1 and 4");
|
||||
// -------------------
|
||||
// | 4 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 1 | 3 |
|
||||
// | | |
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
SwapPaneArgs args{ FocusDirection::Up };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// Our currently focused pane should be `4`
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value());
|
||||
|
||||
// Inspect the tree to make sure we swapped
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_rootPane->_firstChild->_firstChild->Id().value());
|
||||
VERIFY_ARE_EQUAL(firstId, tab->_rootPane->_firstChild->_secondChild->Id().value());
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
Log::Comment(L"Move pane to the right. This should swap panes 2 and 4");
|
||||
// -------------------
|
||||
// | 2 | 4 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 1 | 3 |
|
||||
// | | |
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
SwapPaneArgs args{ FocusDirection::Right };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// Our currently focused pane should be `4`
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value());
|
||||
|
||||
// Inspect the tree to make sure we swapped
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_rootPane->_secondChild->_firstChild->Id().value());
|
||||
VERIFY_ARE_EQUAL(secondId, tab->_rootPane->_firstChild->_firstChild->Id().value());
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
Log::Comment(L"Move pane down. This should swap panes 3 and 4");
|
||||
// -------------------
|
||||
// | 2 | 3 |
|
||||
// | | |
|
||||
// -------------------
|
||||
// | 1 | 4 |
|
||||
// | | |
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
SwapPaneArgs args{ FocusDirection::Down };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
TestOnUIThread([&]() {
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount());
|
||||
// Our currently focused pane should be `4`
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value());
|
||||
|
||||
// Inspect the tree to make sure we swapped
|
||||
VERIFY_ARE_EQUAL(fourthId, tab->_rootPane->_secondChild->_secondChild->Id().value());
|
||||
VERIFY_ARE_EQUAL(thirdId, tab->_rootPane->_secondChild->_firstChild->Id().value());
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::NextMRUTab()
|
||||
{
|
||||
// This is a test for GH#8025 - we want to make sure that we can do both
|
||||
@@ -1342,4 +1552,55 @@ namespace TerminalAppLocalTests
|
||||
});
|
||||
}
|
||||
|
||||
void TabTests::TestClampSwitchToTab()
|
||||
{
|
||||
Log::Comment(L"Test that switching to a tab index higher than the number of tabs just clamps to the last tab.");
|
||||
|
||||
auto page = _commonSetup();
|
||||
VERIFY_IS_NOT_NULL(page);
|
||||
|
||||
Log::Comment(L"Create a second tab");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 1 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
||||
|
||||
Log::Comment(L"Create a third tab");
|
||||
TestOnUIThread([&page]() {
|
||||
NewTerminalArgs newTerminalArgs{ 2 };
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
});
|
||||
VERIFY_ARE_EQUAL(3u, page->_tabs.Size());
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedTabIndexOpt{ page->_GetFocusedTabIndex() };
|
||||
VERIFY_IS_TRUE(focusedTabIndexOpt.has_value());
|
||||
VERIFY_ARE_EQUAL(2u, focusedTabIndexOpt.value());
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Switch to the first tab");
|
||||
page->_SelectTab(0);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedTabIndexOpt{ page->_GetFocusedTabIndex() };
|
||||
|
||||
VERIFY_IS_TRUE(focusedTabIndexOpt.has_value());
|
||||
VERIFY_ARE_EQUAL(0u, focusedTabIndexOpt.value());
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Switch to the tab 6, which is greater than number of tabs. This should switch to the third tab");
|
||||
page->_SelectTab(6);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
auto focusedTabIndexOpt{ page->_GetFocusedTabIndex() };
|
||||
VERIFY_IS_TRUE(focusedTabIndexOpt.has_value());
|
||||
VERIFY_ARE_EQUAL(2u, focusedTabIndexOpt.value());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore">
|
||||
<HintPath>$(OpenConsoleDir)\packages\Microsoft.Taef.10.58.210305002\lib\Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore.winmd</HintPath>
|
||||
<HintPath>$(OpenConsoleDir)\packages\Microsoft.Taef.10.60.210621002\lib\Microsoft.VisualStudio.TestPlatform.TestExecutor.WinRTCore.winmd</HintPath>
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
|
||||
<!-- This path is _relative to the .winmd_ -->
|
||||
|
||||
@@ -172,7 +172,6 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
_desiredFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8, false },
|
||||
_uiaProvider{ nullptr },
|
||||
_uiaProviderInitialized{ false },
|
||||
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
|
||||
_pfnWriteCallback{ nullptr },
|
||||
_multiClickTime{ 500 } // this will be overwritten by the windows system double-click time
|
||||
@@ -324,26 +323,22 @@ void HwndTerminal::_UpdateFont(int newDpi)
|
||||
|
||||
IRawElementProviderSimple* HwndTerminal::_GetUiaProvider() noexcept
|
||||
{
|
||||
if (nullptr == _uiaProvider && !_uiaProviderInitialized)
|
||||
// If TermControlUiaProvider throws during construction,
|
||||
// we don't want to try constructing an instance again and again.
|
||||
// _uiaProviderInitialized helps us prevent this.
|
||||
if (!_uiaProviderInitialized)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock;
|
||||
try
|
||||
{
|
||||
#pragma warning(suppress : 26441) // The lock is named, this appears to be a false positive
|
||||
lock = _terminal->LockForWriting();
|
||||
if (_uiaProviderInitialized)
|
||||
{
|
||||
return _uiaProvider.Get();
|
||||
}
|
||||
|
||||
auto lock = _terminal->LockForWriting();
|
||||
LOG_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, this->GetUiaData(), this));
|
||||
_uiaProviderInitialized = true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_HR(wil::ResultFromCaughtException());
|
||||
_uiaProvider = nullptr;
|
||||
}
|
||||
_uiaProviderInitialized = true;
|
||||
}
|
||||
|
||||
return _uiaProvider.Get();
|
||||
|
||||
@@ -73,7 +73,6 @@ private:
|
||||
FontInfoDesired _desiredFont;
|
||||
FontInfo _actualFont;
|
||||
int _currentDpi;
|
||||
bool _uiaProviderInitialized;
|
||||
std::function<void(wchar_t*)> _pfnWriteCallback;
|
||||
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
|
||||
|
||||
@@ -83,6 +82,7 @@ private:
|
||||
std::unique_ptr<::Microsoft::Console::Render::DxEngine> _renderEngine;
|
||||
|
||||
bool _focused{ false };
|
||||
bool _uiaProviderInitialized{ false };
|
||||
|
||||
std::chrono::milliseconds _multiClickTime;
|
||||
unsigned int _multiClickCounter{};
|
||||
|
||||
@@ -28,8 +28,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// This is a private constructor to be used in unit tests, where we don't
|
||||
// want each Monarch to necessarily use the current PID.
|
||||
// This constructor is intended to be used in unit tests,
|
||||
// but we need to make it public in order to use make_self
|
||||
// in the tests. It's not exposed through the idl though
|
||||
// so it's not _truly_ fully public which should be acceptable.
|
||||
Monarch::Monarch(const uint64_t testPID) :
|
||||
_ourPID{ testPID }
|
||||
{
|
||||
@@ -78,6 +80,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows });
|
||||
peasant.RenameRequested({ this, &Monarch::_renameRequested });
|
||||
|
||||
peasant.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
|
||||
peasant.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
|
||||
peasant.QuitAllRequested({ this, &Monarch::_handleQuitAll });
|
||||
|
||||
_peasants[newPeasantsId] = peasant;
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
@@ -86,6 +92,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
_WindowCreatedHandlers(nullptr, nullptr);
|
||||
return newPeasantsId;
|
||||
}
|
||||
catch (...)
|
||||
@@ -102,6 +110,74 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gives the host process an opportunity to run any pre-close logic then
|
||||
// requests all peasants to close.
|
||||
// Arguments:
|
||||
// - <none> used
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
// Let the process hosting the monarch run any needed logic before
|
||||
// closing all windows.
|
||||
_QuitAllRequestedHandlers(*this, nullptr);
|
||||
|
||||
// Tell all peasants to exit.
|
||||
const auto callback = [&](const auto& /*id*/, const auto& p) {
|
||||
p.Quit();
|
||||
};
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_handleQuitAll_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not close"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
|
||||
_forEachPeasant(callback, onError);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Tells the monarch that a peasant is being closed.
|
||||
// Arguments:
|
||||
// - peasantId: the id of the peasant
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::SignalClose(const uint64_t peasantId)
|
||||
{
|
||||
_peasants.erase(peasantId);
|
||||
_WindowClosedHandlers(nullptr, nullptr);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Counts the number of living peasants.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the number of active peasants.
|
||||
uint64_t Monarch::GetNumberOfPeasants()
|
||||
{
|
||||
auto num = 0;
|
||||
auto callback = [&](const auto& /*id*/, const auto& p) {
|
||||
// Check that the peasant is alive, and if so increment the count
|
||||
p.GetID();
|
||||
num += 1;
|
||||
};
|
||||
auto onError = [](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_GetNumberOfPeasants_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not enumerate"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
|
||||
_forEachPeasant(callback, onError);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Event handler for the Peasant::WindowActivated event. Used as an
|
||||
// opportunity for us to update our internal stack of the "most recent
|
||||
@@ -201,6 +277,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
_clearOldMruEntries(id);
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_lookupPeasantIdForName",
|
||||
TraceLoggingWideString(std::wstring{ name }.c_str(), "name", "the name we're looking for"),
|
||||
TraceLoggingUInt64(result, "peasantID", "the ID of the peasant with that name"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -602,39 +684,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
return *result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper for doing something on each and every peasant, with no regard
|
||||
// for if the peasant is living or dead.
|
||||
// - We'll try calling callback on every peasant.
|
||||
// - If any single peasant is dead, then we'll call errorCallback, and move on.
|
||||
// - We're taking an errorCallback here, because the thing we usually want
|
||||
// to do is TraceLog a message, but TraceLoggingWrite is actually a macro
|
||||
// that _requires_ the second arg to be a string literal. It can't just be
|
||||
// a variable.
|
||||
// Arguments:
|
||||
// - callback: The function to call on each peasant
|
||||
// - errorCallback: The function to call if a peasant is dead.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::_forAllPeasantsIgnoringTheDead(std::function<void(const Remoting::IPeasant&, const uint64_t)> callback,
|
||||
std::function<void(const uint64_t)> errorCallback)
|
||||
{
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(p, id);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
// If this fails, we don't _really_ care. Just move on to the
|
||||
// next one. Someone else will clean up the dead peasant.
|
||||
errorCallback(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This is an event handler for the IdentifyWindowsRequested event. A
|
||||
// Peasant may raise that event if they want _all_ windows to identify
|
||||
@@ -649,17 +698,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
// Notify all the peasants to display their ID.
|
||||
auto callback = [](auto&& p, auto&& /*id*/) {
|
||||
const auto callback = [&](const auto& /*id*/, const auto& p) {
|
||||
p.DisplayWindowId();
|
||||
};
|
||||
auto onError = [](auto&& id) {
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_identifyWindows_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
_forAllPeasantsIgnoringTheDead(callback, onError);
|
||||
|
||||
_forEachPeasant(callback, onError);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -732,24 +782,55 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
try
|
||||
{
|
||||
args.FoundMatch(false);
|
||||
|
||||
// If a WindowID is provided from the args, use that first.
|
||||
uint64_t windowId = 0;
|
||||
// If no name was provided, then just summon the MRU window.
|
||||
if (searchedForName.empty())
|
||||
if (args.WindowID())
|
||||
{
|
||||
// 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);
|
||||
windowId = args.WindowID().Value();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find a peasant that currently has this name
|
||||
windowId = _lookupPeasantIdForName(searchedForName);
|
||||
// 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);
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonWindow_Success",
|
||||
TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"),
|
||||
TraceLoggingUInt64(windowId, "peasantID", "The id of the window we tried to summon"),
|
||||
TraceLoggingBoolean(args.OnCurrentDesktop(), "OnCurrentDesktop", "true iff the window needs to be on the current virtual desktop"),
|
||||
TraceLoggingBoolean(args.SummonBehavior().MoveToCurrentDesktop(), "MoveToCurrentDesktop", "if true, move the window to the current virtual desktop"),
|
||||
TraceLoggingBoolean(args.SummonBehavior().ToggleVisibility(), "ToggleVisibility", "true if we should toggle the visibility of the window"),
|
||||
TraceLoggingUInt32(args.SummonBehavior().DropdownDuration(), "DropdownDuration", "the duration to dropdown the window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonWindow_NoPeasant",
|
||||
TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"),
|
||||
TraceLoggingUInt64(windowId, "peasantID", "The id of the window we tried to summon"),
|
||||
TraceLoggingBoolean(args.OnCurrentDesktop(), "OnCurrentDesktop", "true iff the window needs to be on the current virtual desktop"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -762,4 +843,76 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method creates a map of peasant IDs to peasant names
|
||||
// while removing dead peasants.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - A map of peasant IDs to their names.
|
||||
Windows::Foundation::Collections::IVectorView<PeasantInfo> Monarch::GetPeasantInfos()
|
||||
{
|
||||
std::vector<PeasantInfo> names;
|
||||
names.reserve(_peasants.size());
|
||||
|
||||
const auto func = [&](const auto& id, const auto& p) -> void {
|
||||
names.push_back({ id, p.WindowName(), p.ActiveTabTitle() });
|
||||
};
|
||||
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_identifyWindows_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
|
||||
_forEachPeasant(func, onError);
|
||||
|
||||
return winrt::single_threaded_vector<PeasantInfo>(std::move(names)).GetView();
|
||||
}
|
||||
|
||||
bool Monarch::DoesQuakeWindowExist()
|
||||
{
|
||||
bool result = false;
|
||||
const auto func = [&](const auto& /*id*/, const auto& p) {
|
||||
if (p.WindowName() == QuakeWindowName)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
// continue if we didn't get a positive result
|
||||
return !result;
|
||||
};
|
||||
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_DoesQuakeWindowExist_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not ask for its name"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
|
||||
_forEachPeasant(func, onError);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Monarch::SummonAllWindows()
|
||||
{
|
||||
const auto func = [&](const auto& /*id*/, const auto& p) {
|
||||
SummonWindowBehavior args{};
|
||||
args.ToggleVisibility(false);
|
||||
p.Summon(args);
|
||||
};
|
||||
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonAll_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not summon"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
|
||||
_forEachPeasant(func, onError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,20 +41,32 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
struct Monarch : public MonarchT<Monarch>
|
||||
{
|
||||
Monarch();
|
||||
Monarch(const uint64_t testPID);
|
||||
~Monarch();
|
||||
|
||||
uint64_t GetPID();
|
||||
|
||||
uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant);
|
||||
void SignalClose(const uint64_t peasantId);
|
||||
|
||||
uint64_t GetNumberOfPeasants();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
|
||||
void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
|
||||
void SummonAllWindows();
|
||||
bool DoesQuakeWindowExist();
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
Monarch(const uint64_t testPID);
|
||||
uint64_t _ourPID;
|
||||
|
||||
uint64_t _nextPeasantID{ 1 };
|
||||
@@ -84,6 +96,70 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
|
||||
|
||||
void _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
|
||||
// Method Description:
|
||||
// - Helper for doing something on each and every peasant.
|
||||
// - We'll try calling func on every peasant.
|
||||
// - If the return type of func is void, it will perform it for all peasants.
|
||||
// - If the return type is a boolean, we'll break out of the loop once func
|
||||
// returns false.
|
||||
// - If any single peasant is dead, then we'll call onError and then add it to a
|
||||
// list of peasants to clean up once the loop ends.
|
||||
// Arguments:
|
||||
// - func: The function to call on each peasant
|
||||
// - onError: The function to call if a peasant is dead.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
template<typename F, typename T>
|
||||
void _forEachPeasant(F&& func, T&& onError)
|
||||
{
|
||||
using Map = decltype(_peasants);
|
||||
using R = std::invoke_result_t<F, Map::key_type, Map::mapped_type>;
|
||||
static constexpr auto IsVoid = std::is_void_v<R>;
|
||||
|
||||
std::vector<uint64_t> peasantsToErase;
|
||||
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
try
|
||||
{
|
||||
if constexpr (IsVoid)
|
||||
{
|
||||
func(id, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!func(id, p))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error& exception)
|
||||
{
|
||||
onError(id);
|
||||
|
||||
if (exception.code() == 0x800706ba) // The RPC server is unavailable.
|
||||
{
|
||||
peasantsToErase.emplace_back(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& id : peasantsToErase)
|
||||
{
|
||||
_peasants.erase(id);
|
||||
_clearOldMruEntries(id);
|
||||
}
|
||||
}
|
||||
|
||||
friend class RemotingUnitTests::RemotingTests;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,18 +28,36 @@ namespace Microsoft.Terminal.Remoting
|
||||
|
||||
Boolean FoundMatch;
|
||||
SummonWindowBehavior SummonBehavior;
|
||||
Windows.Foundation.IReference<UInt64> WindowID;
|
||||
}
|
||||
|
||||
struct PeasantInfo
|
||||
{
|
||||
UInt64 Id;
|
||||
String Name;
|
||||
String TabTitle;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Monarch {
|
||||
Monarch();
|
||||
|
||||
UInt64 GetPID();
|
||||
UInt64 AddPeasant(IPeasant peasant);
|
||||
UInt64 GetNumberOfPeasants();
|
||||
ProposeCommandlineResult ProposeCommandline(CommandlineArgs args);
|
||||
void HandleActivatePeasant(WindowActivatedArgs args);
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
void SignalClose(UInt64 peasantId);
|
||||
|
||||
void SummonAllWindows();
|
||||
Boolean DoesQuakeWindowExist();
|
||||
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
}
|
||||
|
||||
// This is a private constructor to be used in unit tests, where we don't
|
||||
// want each Peasant to necessarily use the current PID.
|
||||
// This constructor is intended to be used in unit tests,
|
||||
// but we need to make it public in order to use make_self
|
||||
// in the tests. It's not exposed through the idl though
|
||||
// so it's not _truly_ fully public which should be acceptable.
|
||||
Peasant::Peasant(const uint64_t testPID) :
|
||||
_ourPID{ testPID }
|
||||
{
|
||||
@@ -31,6 +33,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
uint64_t Peasant::GetID()
|
||||
{
|
||||
return _id;
|
||||
@@ -222,4 +225,68 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::RequestShowTrayIcon()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ShowTrayIconRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_RequestShowTrayIcon",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::RequestHideTrayIcon()
|
||||
{
|
||||
try
|
||||
{
|
||||
_HideTrayIconRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_RequestHideTrayIcon",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::RequestQuitAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
_QuitAllRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_RequestQuit",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::Quit()
|
||||
{
|
||||
try
|
||||
{
|
||||
_QuitRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_Quit",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
void RequestIdentifyWindows();
|
||||
void DisplayWindowId();
|
||||
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
void RequestQuitAll();
|
||||
void Quit();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
|
||||
WINRT_PROPERTY(winrt::hstring, WindowName);
|
||||
WINRT_PROPERTY(winrt::hstring, ActiveTabTitle);
|
||||
|
||||
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs);
|
||||
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs);
|
||||
@@ -40,6 +45,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
|
||||
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
Peasant(const uint64_t testPID);
|
||||
|
||||
@@ -61,9 +61,14 @@ namespace Microsoft.Terminal.Remoting
|
||||
void DisplayWindowId(); // Tells us to display its own ID (which causes a DisplayWindowIdRequested to be raised)
|
||||
|
||||
String WindowName { get; };
|
||||
String ActiveTabTitle { get; };
|
||||
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
||||
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
|
||||
void Summon(SummonWindowBehavior behavior);
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
void RequestQuitAll();
|
||||
void Quit();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
||||
@@ -71,6 +76,10 @@ namespace Microsoft.Terminal.Remoting
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Peasant : IPeasant
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
WINRT_PROPERTY(bool, FoundMatch, false);
|
||||
WINRT_PROPERTY(bool, OnCurrentDesktop, false);
|
||||
WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior);
|
||||
|
||||
WINRT_PROPERTY(Windows::Foundation::IReference<uint64_t>, WindowID);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// monarch!
|
||||
CoRevokeClassObject(_registrationHostClass);
|
||||
_registrationHostClass = 0;
|
||||
SignalClose();
|
||||
_monarchWaitInterrupt.SetEvent();
|
||||
|
||||
// A thread is joinable once it's been started. Basically this just
|
||||
@@ -64,6 +65,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::SignalClose()
|
||||
{
|
||||
if (_monarch)
|
||||
{
|
||||
try
|
||||
{
|
||||
_monarch.SignalClose(_peasant.GetID());
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args)
|
||||
{
|
||||
// If we're the king, we _definitely_ want to process the arguments, we were
|
||||
@@ -250,10 +263,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// Here, we're the king!
|
||||
//
|
||||
// This is where you should do any additional setup that might need to be
|
||||
// done when we become the king. THis will be called both for the first
|
||||
// done when we become the king. This will be called both for the first
|
||||
// window, and when the current monarch dies.
|
||||
|
||||
_monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers });
|
||||
_monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers });
|
||||
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
|
||||
_monarch.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
|
||||
_monarch.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
|
||||
_monarch.QuitAllRequested([this](auto&&, auto&&) { _QuitAllRequestedHandlers(*this, nullptr); });
|
||||
|
||||
_BecameMonarchHandlers(*this, nullptr);
|
||||
}
|
||||
@@ -509,4 +527,79 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
_monarch.SummonWindow(args);
|
||||
}
|
||||
|
||||
void WindowManager::SummonAllWindows()
|
||||
{
|
||||
if constexpr (Feature_TrayIcon::IsEnabled())
|
||||
{
|
||||
_monarch.SummonAllWindows();
|
||||
}
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> WindowManager::GetPeasantInfos()
|
||||
{
|
||||
// We should only get called when we're the monarch since the monarch
|
||||
// is the only one that knows about all peasants.
|
||||
return _monarch.GetPeasantInfos();
|
||||
}
|
||||
|
||||
uint64_t WindowManager::GetNumberOfPeasants()
|
||||
{
|
||||
if (_monarch)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _monarch.GetNumberOfPeasants();
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to show a tray icon.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestShowTrayIcon()
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
_peasant.RequestShowTrayIcon();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to hide its tray icon.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestHideTrayIcon()
|
||||
{
|
||||
auto strongThis{ get_strong() };
|
||||
co_await winrt::resume_background();
|
||||
_peasant.RequestHideTrayIcon();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to quit all windows.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestQuitAll()
|
||||
{
|
||||
auto strongThis{ get_strong() };
|
||||
co_await winrt::resume_background();
|
||||
_peasant.RequestQuitAll();
|
||||
}
|
||||
|
||||
bool WindowManager::DoesQuakeWindowExist()
|
||||
{
|
||||
return _monarch.DoesQuakeWindowExist();
|
||||
}
|
||||
|
||||
void WindowManager::UpdateActiveTabTitle(winrt::hstring title)
|
||||
{
|
||||
winrt::get_self<implementation::Peasant>(_peasant)->ActiveTabTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow();
|
||||
bool IsMonarch();
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
void SignalClose();
|
||||
|
||||
void SummonAllWindows();
|
||||
uint64_t GetNumberOfPeasants();
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
|
||||
|
||||
winrt::fire_and_forget RequestShowTrayIcon();
|
||||
winrt::fire_and_forget RequestHideTrayIcon();
|
||||
winrt::fire_and_forget RequestQuitAll();
|
||||
bool DoesQuakeWindowExist();
|
||||
void UpdateActiveTabTitle(winrt::hstring title);
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
bool _shouldCreateWindow{ false };
|
||||
|
||||
@@ -8,11 +8,25 @@ namespace Microsoft.Terminal.Remoting
|
||||
{
|
||||
WindowManager();
|
||||
void ProposeCommandline(CommandlineArgs args);
|
||||
void SignalClose();
|
||||
Boolean ShouldCreateWindow { get; };
|
||||
IPeasant CurrentWindow();
|
||||
Boolean IsMonarch { get; };
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
void SummonAllWindows();
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
UInt64 GetNumberOfPeasants();
|
||||
void RequestQuitAll();
|
||||
void UpdateActiveTabTitle(String title);
|
||||
Boolean DoesQuakeWindowExist();
|
||||
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
Color="{ThemeResource SystemErrorTextColor}" />
|
||||
|
||||
<!-- Suppress top padding -->
|
||||
<Thickness x:Key="TabViewHeaderPadding">8,0,8,0</Thickness>
|
||||
<Thickness x:Key="TabViewHeaderPadding">9,0,8,0</Thickness>
|
||||
|
||||
<!-- Remove when implementing WinUI 2.6 -->
|
||||
<Thickness x:Key="FlyoutContentPadding">12</Thickness>
|
||||
@@ -55,12 +55,18 @@
|
||||
<!-- Define resources for Dark mode here -->
|
||||
<SolidColorBrush x:Key="TabViewBackground"
|
||||
Color="#FF333333" />
|
||||
|
||||
<SolidColorBrush x:Key="UnfocusedBorderBrush"
|
||||
Color="#FF333333" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<!-- Define resources for Light mode here -->
|
||||
<SolidColorBrush x:Key="TabViewBackground"
|
||||
Color="#FFCCCCCC" />
|
||||
|
||||
<SolidColorBrush x:Key="UnfocusedBorderBrush"
|
||||
Color="#FFCCCCCC" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
@@ -78,7 +78,14 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
CloseWindow();
|
||||
CloseWindow(false);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleQuit(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
RequestQuit();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
@@ -143,6 +150,20 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMovePane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args == nullptr)
|
||||
{
|
||||
args.Handled(false);
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
|
||||
{
|
||||
auto moved = _MovePane(realArgs.TabIndex());
|
||||
args.Handled(moved);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSplitPane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
@@ -161,6 +182,13 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleSplitOrientation(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
_ToggleSplitOrientation();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
@@ -257,12 +285,12 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (args == nullptr)
|
||||
{
|
||||
_OpenNewTab(nullptr);
|
||||
LOG_IF_FAILED(_OpenNewTab(nullptr));
|
||||
args.Handled(true);
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<NewTabArgs>())
|
||||
{
|
||||
_OpenNewTab(realArgs.TerminalArgs());
|
||||
LOG_IF_FAILED(_OpenNewTab(realArgs.TerminalArgs()));
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
@@ -307,8 +335,29 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
_MoveFocus(realArgs.FocusDirection());
|
||||
args.Handled(true);
|
||||
// Mark as handled only when the move succeeded (e.g. when there
|
||||
// is a pane to move to), otherwise mark as unhandled so the
|
||||
// keychord can propagate to the terminal (GH#6129)
|
||||
const auto moveSucceeded = _MoveFocus(realArgs.FocusDirection());
|
||||
args.Handled(moveSucceeded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSwapPane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SwapPaneArgs>())
|
||||
{
|
||||
if (realArgs.Direction() == FocusDirection::None)
|
||||
{
|
||||
// Do nothing
|
||||
args.Handled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto swapped = _SwapPane(realArgs.Direction());
|
||||
args.Handled(swapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -711,11 +760,10 @@ namespace winrt::TerminalApp::implementation
|
||||
newTerminalArgs = NewTerminalArgs();
|
||||
}
|
||||
|
||||
const auto profileGuid{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
|
||||
// Manually fill in the evaluated profile.
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profileGuid));
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
|
||||
_OpenNewWindow(false, newTerminalArgs);
|
||||
actionArgs.Handled(true);
|
||||
}
|
||||
@@ -833,4 +881,44 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleOpenSystemMenu(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
_OpenSystemMenuHandlers(*this, nullptr);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleClearBuffer(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<ClearBufferArgs>())
|
||||
{
|
||||
if (const auto termControl{ _GetActiveControl() })
|
||||
{
|
||||
termControl.ClearBuffer(realArgs.Clear());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMultipleActions(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<MultipleActionsArgs>())
|
||||
{
|
||||
for (const auto& action : realArgs.Actions())
|
||||
{
|
||||
_actionDispatch->DoAction(action);
|
||||
}
|
||||
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user