Compare commits

...

22 Commits

Author SHA1 Message Date
Dustin L. Howett
99fa2805f0 Migrate spelling-0.0.21 changes from main 2021-04-23 10:03:28 -07:00
Dustin L. Howett
efa955719e Migrate spelling-0.0.19 changes from main 2021-04-23 10:03:28 -07:00
Carlos Zamora
f256a551e8 Introduce template for key bindings page 2021-04-23 16:23:37 -07:00
Carlos Zamora
0564cae164 polish the actions page a bit w/ sorting and removing unnecessary converter 2021-04-23 14:29:09 -07:00
Carlos Zamora
a961fc26af PR feedback; copy parents too 2021-04-23 13:59:22 -07:00
Carlos Zamora
17fc18b893 fix build 2021-04-22 17:10:21 -07:00
Carlos Zamora
d6f3864242 fix old reference to 'cmd.Action' 2021-04-22 11:41:59 -07:00
Carlos Zamora
3d3f355553 fix all tests and dropdown bug 2021-04-22 11:06:19 -07:00
Carlos Zamora
dc1de34bb7 fix failing tests; We're good to go!! 2021-04-22 11:06:19 -07:00
Carlos Zamora
8507ef0471 rename cmd.Action; move HashProperty; fix most tests 2021-04-22 11:06:19 -07:00
Carlos Zamora
4fde1b0891 Apply Griese's PR feedback 2021-04-22 11:06:19 -07:00
Carlos Zamora
d1b71eddb6 update dictionary 2021-04-22 11:06:19 -07:00
Carlos Zamora
9a1af76830 code format and minor polish 2021-04-22 11:06:19 -07:00
Carlos Zamora
8840bc58f7 fix nested commands 2021-04-22 11:06:19 -07:00
Carlos Zamora
a00b84f86a fix most tests. Missing 8 DeserializationTests and 5 SettingsTests 2021-04-22 11:06:19 -07:00
Carlos Zamora
bc996c0170 add action hashing; update command; fix test DllMain error 2021-04-22 11:06:19 -07:00
Carlos Zamora
460f91477c Introduce ActionMap to Terminal Settings Model 2021-04-22 11:05:51 -07:00
Carlos Zamora
9de4f58999 add [] notation support to converter 2021-04-22 11:03:12 -07:00
Carlos Zamora
68984c811b xaml format 2021-04-22 11:03:12 -07:00
Carlos Zamora
c0c5a49ffd fix nested key chords 2021-04-22 11:03:12 -07:00
Carlos Zamora
42c43ff2e9 remove KeyChordText from Command 2021-04-22 11:03:11 -07:00
Carlos Zamora
e8dd48625c Give Command ownership over KeyChord 2021-04-22 11:03:11 -07:00
89 changed files with 3584 additions and 482921 deletions

15
.github/actions/spelling/README.md vendored Normal file
View 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.

48
.github/actions/spelling/advice.md vendored Normal file
View File

@@ -0,0 +1,48 @@
<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details>
<summary>
:pencil2: Contributor please read this
</summary>
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. 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:
* ... **misspelled**, then please *correct* them instead of using the command.
* ... *names*, please add them to `.github/actions/spelling/allow/names.txt`.
* ... APIs, you can add them to a file in `.github/actions/spelling/allow/`.
* ... just things you're using, please add them to an appropriate file in `.github/actions/spelling/expect/`.
* ... tokens you only need in one place and shouldn't *generally be used*, you can add an item in an appropriate file in `.github/actions/spelling/patterns/`.
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>If the flagged items are :exploding_head: false positives</summary>
If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).
Please add a file path to the `excludes.txt` file matching the containing file.
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](
../tree/HEAD/README.md) (on whichever branch you're using).
* 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>

View File

@@ -1,27 +0,0 @@
<details>
<summary>
:pencil2: Contributor please read this
</summary>
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...
If the listed items are:
* ... **misspelled**, then please *correct* them instead of using the command.
* ... *names*, please add them to `.github/actions/spelling/dictionary/names.txt`.
* ... APIs, you can add them to a file in `.github/actions/spelling/dictionary/`.
* ... just things you're using, please add them to an appropriate file in `.github/actions/spelling/expect/`.
* ... tokens you only need in one place and shouldn't *generally be used*, you can add an item in an appropriate file in `.github/actions/spelling/patterns/`.
See the `README.md` in each directory for more information.
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
:clamp: If you see a bunch of garbage and it relates to a binary-ish string, please add a file path to the `.github/actions/spelling/excludes.txt` file instead of just accepting the garbage.
File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](https://github.com/microsoft/terminal/blob/main/README.md) (on whichever branch you're using).
</details>

View File

@@ -1,4 +1,4 @@
# Dictionaries are lists of words to accept unconditionally
# Allow files are lists of words to accept unconditionally
While check spelling will complain about an expected word
which is no longer present, you can include things here even if
@@ -8,11 +8,11 @@ E.g., you could include a list of system APIs here, or potential
contributors (so that if a future commit includes their name,
it'll be accepted).
### Files
## Files
| File | Description |
| ---- | ----------- |
| [Dictionary](dictionary.txt) | Primary US English dictionary |
| [Allow](allow.txt) | Supplements to the dictionary |
| [Chinese](chinese.txt) | Chinese words |
| [Japanese](japanese.txt) | Japanese words |
| [Microsoft](microsoft.txt) | Microsoft brand items |

108
.github/actions/spelling/allow/allow.txt vendored Normal file
View File

@@ -0,0 +1,108 @@
admins
allcolors
Apc
apc
breadcrumb
breadcrumbs
bsd
calt
ccmp
changelog
clickable
clig
CMMI
copyable
cybersecurity
dalet
Dcs
dcs
dialytika
dje
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

View File

@@ -1,23 +1,48 @@
ACCEPTFILES
ACCESSDENIED
acl
aclapi
alignas
alignof
APPLYTOSUBMENUS
appxrecipe
bitfield
bitfields
BUILDBRANCH
BUILDMSG
BUILDNUMBER
BYCOMMAND
BYPOSITION
charconv
CLASSNOTAVAILABLE
CLOSEAPP
cmdletbinding
colspan
COLORPROPERTY
colspan
COMDLG
commandlinetoargv
comparand
cstdint
CXICON
CYICON
D2DERR_SHADER_COMPILE_FAILED
Dacl
dataobject
dcomp
DERR
dlldata
DNE
DONTADDTORECENT
DWMSBT
DWMWA
DWMWA
DWORDLONG
endfor
ENDSESSION
enumset
environstrings
EXPCMDFLAGS
EXPCMDSTATE
filetime
FILTERSPEC
FORCEFILESYSTEM
FORCEMINIMIZE
@@ -26,11 +51,16 @@ fullkbd
futex
GETDESKWALLPAPER
GETHIGHCONTRAST
GETMOUSEHOVERTIME
Hashtable
HIGHCONTRASTON
HIGHCONTRASTW
hotkeys
href
hrgn
HTCLOSE
hwinsta
HWINSTA
IActivation
IApp
IAppearance
@@ -39,97 +69,180 @@ IBind
IBox
IClass
IComparable
IComparer
IConnection
ICustom
IDialog
IDirect
IExplorer
IFACEMETHOD
IFile
IGraphics
IInheritable
IMap
IMonarch
IObject
iosfwd
IPackage
IPeasant
ISetup
isspace
IStorage
istream
IStringable
ITab
ITaskbar
itow
IUri
IVirtual
KEYSELECT
LCID
llabs
llu
localtime
lround
Lsa
lsass
LSHIFT
MULTIPLEUSE
LTGRAY
MAINWINDOW
memchr
memicmp
MENUCOMMAND
MENUDATA
MENUINFO
MENUITEMINFOW
mmeapi
MOUSELEAVE
mov
mptt
msappx
MULTIPLEUSE
NCHITTEST
NCLBUTTONDBLCLK
NCMOUSELEAVE
NCMOUSEMOVE
NCRBUTTONDBLCLK
NIF
NIN
NOAGGREGATION
NOASYNC
NOCHANGEDIR
NOPROGRESS
NOREDIRECTIONBITMAP
NOREPEAT
NOTIFYBYPOS
NOTIFYICON
NOTIFYICONDATA
ntprivapi
oaidl
ocidl
ODR
offsetof
ofstream
onefuzz
osver
OSVERSIONINFOEXW
otms
OUTLINETEXTMETRICW
overridable
PACL
PAGESCROLL
PATINVERT
PEXPLICIT
PICKFOLDERS
pmr
REGCLS
ptstr
QUERYENDSESSION
rcx
REGCLS
RETURNCMD
rfind
ROOTOWNER
roundf
RSHIFT
rx
SACL
schandle
semver
serializer
SETVERSION
SHELLEXECUTEINFOW
shobjidl
SINGLEUSE
SHOWHIDE
SHOWMINIMIZED
SHOWTIP
SINGLEUSE
SIZENS
smoothstep
GETDESKWALLPAPER
SHELLEXECUTEINFOW
snprintf
spsc
sregex
SRWLOC
SRWLOCK
STDCPP
strchr
STDMETHOD
strchr
strcpy
streambuf
strtoul
Stubless
Subheader
Subpage
UPDATEINIFILE
syscall
SYSTEMBACKDROP
TABROW
TASKBARCREATED
TBPF
THEMECHANGED
tlg
TME
tmp
tmpdir
tolower
toupper
TRACKMOUSEEVENT
TTask
TVal
tx
UChar
UFIELD
ULARGE
UOI
UPDATEINIFILE
userenv
USEROBJECTFLAGS
Viewbox
virtualalloc
wcsstr
wcstoui
winmain
winsta
winstamin
wmemcmp
wpc
WSF
wsregex
wwinmain
xchg
XDocument
XElement
xfacet
xhash
XIcon
xiosbase
xlocale
xlocbuf
xlocinfo
xlocmes
xlocmon
xlocnum
xloctime
XMax
xmemory
XParse
xpath
xstddef
xstring
xtree
xutility
YIcon
YMax

View File

@@ -0,0 +1,117 @@
alice
aliceblue
antiquewhite
blanchedalmond
blueviolet
burlywood
cadetblue
cornflowerblue
cornsilk
cyan
darkblue
darkcyan
darkgoldenrod
darkgray
darkgreen
darkgrey
darkkhaki
darkmagenta
darkolivegreen
darkorange
darkorchid
darkred
darksalmon
darkseagreen
darkslateblue
darkslategray
darkslategrey
darkturquoise
darkviolet
deeppink
deepskyblue
dimgray
dimgrey
dodgerblue
firebrick
floralwhite
forestgreen
gainsboro
ghostwhite
greenyellow
hotpink
indian
indianred
lavenderblush
lawngreen
lemonchiffon
lightblue
lightcoral
lightcyan
lightgoldenrod
lightgoldenrodyellow
lightgray
lightgreen
lightgrey
lightpink
lightsalmon
lightseagreen
lightskyblue
lightslateblue
lightslategray
lightslategrey
lightsteelblue
lightyellow
limegreen
mediumaquamarine
mediumblue
mediumorchid
mediumpurple
mediumseagreen
mediumslateblue
mediumspringgreen
mediumturquoise
mediumvioletred
midnightblue
mintcream
mistyrose
navajo
navajowhite
navyblue
oldlace
olivedrab
orangered
palegoldenrod
palegreen
paleturquoise
palevioletred
papayawhip
peachpuff
peru
powderblue
rebecca
rebeccapurple
rosybrown
royalblue
saddlebrown
sandybrown
seagreen
sienna
skyblue
slateblue
slategray
slategrey
springgreen
steelblue
violetred
webgray
webgreen
webgrey
webmaroon
webpurple
whitesmoke
xaroon
xray
xreen
xrey
xurple
yellowgreen

View File

@@ -7,3 +7,4 @@ Iosevka
MDL
Monofur
Segoe
wght

11
.github/actions/spelling/allow/math.txt vendored Normal file
View File

@@ -0,0 +1,11 @@
atan
CPrime
HBar
HPrime
isnan
LPrime
LStep
powf
RSub
sqrtf
ULP

View File

@@ -1,17 +1,25 @@
ACLs
ADMINS
advapi
altform
altforms
appendwttlogging
appx
appxbundle
appxerror
appxmanifest
ATL
backplating
bitmaps
BOMs
CPLs
CPRs
cpptools
cppvsdbg
CPRs
cryptbase
DACL
DACLs
defaultlib
diffs
disposables
dotnetfeed
@@ -19,14 +27,24 @@ DTDs
DWINRT
enablewttlogging
Intelli
IVisual
libucrt
libucrtd
LKG
LOCKFILE
Lxss
mfcribbon
microsoft
microsoftonline
MSAA
msixbundle
MSVC
MSVCP
muxc
netcore
Onefuzz
osgvsowi
PFILETIME
pgc
pgo
pgosweep
@@ -34,20 +52,31 @@ powerrename
powershell
propkey
pscustomobject
QWORD
regedit
robocopy
SACLs
sdkddkver
Shobjidl
Skype
SRW
sxs
Sysinternals
sysnative
systemroot
taskkill
tasklist
tdbuildteamid
ucrt
ucrtd
unvirtualized
VCRT
vcruntime
Virtualization
visualstudio
vscode
VSTHRD
winsdkver
wlk
wslpath
wtl

View File

@@ -1,24 +1,30 @@
Anup
austdi
arkthur
Ballmer
bhoj
Bhojwani
Bluloco
carlos
dhowett
Diviness
dsafa
duhowett
DXP
ekg
eryksun
ethanschoonover
Firefox
Gatta
glsl
Gravell
Grie
Griese
Hernan
Howett
Illhardt
iquilezles
italo
jantari
jerrysh
Kaiyu
@@ -30,8 +36,11 @@ Kourosh
kowalczyk
leonmsft
Lepilleur
lhecker
lukesampson
Macbook
Manandhar
masserano
mbadolato
Mehrain
menger
@@ -51,6 +60,7 @@ oldnewthing
opengl
osgwiki
pabhojwa
panos
paulcam
pauldotknopf
PGP
@@ -59,18 +69,23 @@ Rincewind
rprichard
Schoonover
shadertoy
Shomnipotence
simioni
Somuah
sonph
sonpham
stakx
talo
thereses
Walisch
WDX
Wellons
Wirt
Wojciech
zadjii
Zamor
zamora
Zamora
zamora
Zoey
zorio
Zverovich

View 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
(['"]|&quot;)[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-)/

View File

@@ -1,782 +0,0 @@
snow
ghost-white
ghostwhite
white-smoke
whitesmoke
gainsboro
floral-white
floralwhite
old-lace
oldlace
linen
antique-white
antiquewhite
papaya-whip
papayawhip
blanched-almond
blanchedalmond
bisque
peach-puff
peachpuff
navajo-white
navajowhite
moccasin
cornsilk
ivory
lemon-chiffon
lemonchiffon
seashell
honeydew
mint-cream
mintcream
azure
alice-blue
aliceblue
lavender
lavender-blush
lavenderblush
misty-rose
mistyrose
white
black
dark-slate-gray
darkslategray
dark-slate-grey
darkslategrey
dim-gray
dimgray
dim-grey
dimgrey
slate-gray
slategray
slate-grey
slategrey
light-slate-gray
lightslategray
light-slate-grey
lightslategrey
gray
grey
xray
x11gray
xrey
x11grey
web-gray
webgray
web-grey
webgrey
light-grey
lightgrey
light-gray
lightgray
midnight-blue
midnightblue
navy
navy-blue
navyblue
cornflower-blue
cornflowerblue
dark-slate-blue
darkslateblue
slate-blue
slateblue
medium-slate-blue
mediumslateblue
light-slate-blue
lightslateblue
medium-blue
mediumblue
royal-blue
royalblue
blue
dodger-blue
dodgerblue
deep-sky-blue
deepskyblue
sky-blue
skyblue
light-sky-blue
lightskyblue
steel-blue
steelblue
light-steel-blue
lightsteelblue
light-blue
lightblue
powder-blue
powderblue
pale-turquoise
paleturquoise
dark-turquoise
darkturquoise
medium-turquoise
mediumturquoise
turquoise
cyan
aqua
light-cyan
lightcyan
cadet-blue
cadetblue
medium-aquamarine
mediumaquamarine
aquamarine
dark-green
darkgreen
dark-olive-green
darkolivegreen
dark-sea-green
darkseagreen
sea-green
seagreen
medium-sea-green
mediumseagreen
light-sea-green
lightseagreen
pale-green
palegreen
spring-green
springgreen
lawn-green
lawngreen
green
lime
xreen
x11green
web-green
webgreen
chartreuse
medium-spring-green
mediumspringgreen
green-yellow
greenyellow
lime-green
limegreen
yellow-green
yellowgreen
forest-green
forestgreen
olive-drab
olivedrab
dark-khaki
darkkhaki
khaki
pale-goldenrod
palegoldenrod
light-goldenrod-yellow
lightgoldenrodyellow
light-yellow
lightyellow
yellow
gold
light-goldenrod
lightgoldenrod
goldenrod
dark-goldenrod
darkgoldenrod
rosy-brown
rosybrown
indian-red
indianred
saddle-brown
saddlebrown
sienna
peru
burlywood
beige
wheat
sandy-brown
sandybrown
tan
chocolate
firebrick
brown
dark-salmon
darksalmon
salmon
light-salmon
lightsalmon
orange
dark-orange
darkorange
coral
light-coral
lightcoral
tomato
orange-red
orangered
red
hot-pink
hotpink
deep-pink
deeppink
pink
light-pink
lightpink
pale-violet-red
palevioletred
maroon
xaroon
x11maroon
web-maroon
webmaroon
medium-violet-red
mediumvioletred
violet-red
violetred
magenta
fuchsia
violet
plum
orchid
medium-orchid
mediumorchid
dark-orchid
darkorchid
dark-violet
darkviolet
blue-violet
blueviolet
purple
xurple
x11purple
web-purple
webpurple
medium-purple
mediumpurple
thistle
snow1
snow2
snow3
snow4
seashell1
seashell2
seashell3
seashell4
antiquewhite1
antiquewhite2
antiquewhite3
antiquewhite4
bisque1
bisque2
bisque3
bisque4
peachpuff1
peachpuff2
peachpuff3
peachpuff4
navajowhite1
navajowhite2
navajowhite3
navajowhite4
lemonchiffon1
lemonchiffon2
lemonchiffon3
lemonchiffon4
cornsilk1
cornsilk2
cornsilk3
cornsilk4
ivory1
ivory2
ivory3
ivory4
honeydew1
honeydew2
honeydew3
honeydew4
lavenderblush1
lavenderblush2
lavenderblush3
lavenderblush4
mistyrose1
mistyrose2
mistyrose3
mistyrose4
azure1
azure2
azure3
azure4
slateblue1
slateblue2
slateblue3
slateblue4
royalblue1
royalblue2
royalblue3
royalblue4
blue1
blue2
blue3
blue4
dodgerblue1
dodgerblue2
dodgerblue3
dodgerblue4
steelblue1
steelblue2
steelblue3
steelblue4
deepskyblue1
deepskyblue2
deepskyblue3
deepskyblue4
skyblue1
skyblue2
skyblue3
skyblue4
lightskyblue1
lightskyblue2
lightskyblue3
lightskyblue4
slategray1
slategray2
slategray3
slategray4
lightsteelblue1
lightsteelblue2
lightsteelblue3
lightsteelblue4
lightblue1
lightblue2
lightblue3
lightblue4
lightcyan1
lightcyan2
lightcyan3
lightcyan4
paleturquoise1
paleturquoise2
paleturquoise3
paleturquoise4
cadetblue1
cadetblue2
cadetblue3
cadetblue4
turquoise1
turquoise2
turquoise3
turquoise4
cyan1
cyan2
cyan3
cyan4
darkslategray1
darkslategray2
darkslategray3
darkslategray4
aquamarine1
aquamarine2
aquamarine3
aquamarine4
darkseagreen1
darkseagreen2
darkseagreen3
darkseagreen4
seagreen1
seagreen2
seagreen3
seagreen4
palegreen1
palegreen2
palegreen3
palegreen4
springgreen1
springgreen2
springgreen3
springgreen4
green1
green2
green3
green4
chartreuse1
chartreuse2
chartreuse3
chartreuse4
olivedrab1
olivedrab2
olivedrab3
olivedrab4
darkolivegreen1
darkolivegreen2
darkolivegreen3
darkolivegreen4
khaki1
khaki2
khaki3
khaki4
lightgoldenrod1
lightgoldenrod2
lightgoldenrod3
lightgoldenrod4
lightyellow1
lightyellow2
lightyellow3
lightyellow4
yellow1
yellow2
yellow3
yellow4
gold1
gold2
gold3
gold4
goldenrod1
goldenrod2
goldenrod3
goldenrod4
darkgoldenrod1
darkgoldenrod2
darkgoldenrod3
darkgoldenrod4
rosybrown1
rosybrown2
rosybrown3
rosybrown4
indianred1
indianred2
indianred3
indianred4
sienna1
sienna2
sienna3
sienna4
burlywood1
burlywood2
burlywood3
burlywood4
wheat1
wheat2
wheat3
wheat4
tan1
tan2
tan3
tan4
chocolate1
chocolate2
chocolate3
chocolate4
firebrick1
firebrick2
firebrick3
firebrick4
brown1
brown2
brown3
brown4
salmon1
salmon2
salmon3
salmon4
lightsalmon1
lightsalmon2
lightsalmon3
lightsalmon4
orange1
orange2
orange3
orange4
darkorange1
darkorange2
darkorange3
darkorange4
coral1
coral2
coral3
coral4
tomato1
tomato2
tomato3
tomato4
orangered1
orangered2
orangered3
orangered4
red1
red2
red3
red4
deeppink1
deeppink2
deeppink3
deeppink4
hotpink1
hotpink2
hotpink3
hotpink4
pink1
pink2
pink3
pink4
lightpink1
lightpink2
lightpink3
lightpink4
palevioletred1
palevioletred2
palevioletred3
palevioletred4
maroon1
maroon2
maroon3
maroon4
violetred1
violetred2
violetred3
violetred4
magenta1
magenta2
magenta3
magenta4
orchid1
orchid2
orchid3
orchid4
plum1
plum2
plum3
plum4
mediumorchid1
mediumorchid2
mediumorchid3
mediumorchid4
darkorchid1
darkorchid2
darkorchid3
darkorchid4
purple1
purple2
purple3
purple4
mediumpurple1
mediumpurple2
mediumpurple3
mediumpurple4
thistle1
thistle2
thistle3
thistle4
gray0
grey0
gray1
grey1
gray2
grey2
gray3
grey3
gray4
grey4
gray5
grey5
gray6
grey6
gray7
grey7
gray8
grey8
gray9
grey9
gray10
grey10
gray11
grey11
gray12
grey12
gray13
grey13
gray14
grey14
gray15
grey15
gray16
grey16
gray17
grey17
gray18
grey18
gray19
grey19
gray20
grey20
gray21
grey21
gray22
grey22
gray23
grey23
gray24
grey24
gray25
grey25
gray26
grey26
gray27
grey27
gray28
grey28
gray29
grey29
gray30
grey30
gray31
grey31
gray32
grey32
gray33
grey33
gray34
grey34
gray35
grey35
gray36
grey36
gray37
grey37
gray38
grey38
gray39
grey39
gray40
grey40
gray41
grey41
gray42
grey42
gray43
grey43
gray44
grey44
gray45
grey45
gray46
grey46
gray47
grey47
gray48
grey48
gray49
grey49
gray50
grey50
gray51
grey51
gray52
grey52
gray53
grey53
gray54
grey54
gray55
grey55
gray56
grey56
gray57
grey57
gray58
grey58
gray59
grey59
gray60
grey60
gray61
grey61
gray62
grey62
gray63
grey63
gray64
grey64
gray65
grey65
gray66
grey66
gray67
grey67
gray68
grey68
gray69
grey69
gray70
grey70
gray71
grey71
gray72
grey72
gray73
grey73
gray74
grey74
gray75
grey75
gray76
grey76
gray77
grey77
gray78
grey78
gray79
grey79
gray80
grey80
gray81
grey81
gray82
grey82
gray83
grey83
gray84
grey84
gray85
grey85
gray86
grey86
gray87
grey87
gray88
grey88
gray89
grey89
gray90
grey90
gray91
grey91
gray92
grey92
gray93
grey93
gray94
grey94
gray95
grey95
gray96
grey96
gray97
grey97
gray98
grey98
gray99
grey99
gray100
grey100
dark-grey
darkgrey
dark-gray
darkgray
dark-blue
darkblue
dark-cyan
darkcyan
dark-magenta
darkmagenta
dark-red
darkred
light-green
lightgreen
crimson
indigo
olive
rebecca-purple
rebeccapurple
silver
teal

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +0,0 @@
powf
sqrtf
isnan

View File

@@ -1,27 +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$
@@ -29,39 +41,77 @@ SUMS$
\.lock$
\.map$
\.min\..
\.mod$
\.mp3$
\.mp4$
\.o$
\.ocf$
\.otf$
\.pbxproj$
\.pdf$
\.pem$
(?:(?i)\.png$)
\.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/
^oss/
^doc/reference/UTF8-torture-test\.txt$
^src/interactivity/onecore/BgfxEngine\.
^src/renderer/wddmcon/WddmConRenderer\.
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
^src/types/ut_types/UtilsTests.cpp$
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
^\.github/actions/spelling/
^\.gitignore$
^doc/reference/master-sequence-list.csv$
^\XamlStyler.json$
^doc/reference/UTF8-torture-test\.txt$
^oss/
^src/host/ft_uia/run\.bat$
^src/host/runft\.bat$
^src/host/runut\.bat$
^src/interactivity/onecore/BgfxEngine\.
^src/renderer/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$
^src/tools/pixels/pixels\.bat$
^src/tools/texttests/fira\.txt$
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
^src/types/ut_types/UtilsTests.cpp$
^tools/ReleaseEngineering/ServicingPipeline.ps1$
ignore$
SUMS$

View File

@@ -1,17 +1,23 @@
AAAa
AAAAA
AAAAAAAAAAAAA
AAAAAABBBBBBCCC
AAAAABBBBBBCCC
abcd
abcd
abcde
abcdef
ABCDEFG
ABCDEFGH
ABCDEFGHIJ
abcdefghijk
ABCDEFGHIJKLMNO
abcdefghijklmnop
ABCDEFGHIJKLMNOPQRST
abcdefghijklmnopqrstuvwxyz
ABCG
ABE
abf
BBBBB
BBBBBBBB
BBBBBCCC
BBBBCCCCC
BBGGRR
BBBBBBBBBBBBBBDDDD
EFG
EFGh
QQQQQQQQQQABCDEFGHIJ
@@ -20,7 +26,6 @@ QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
qrstuvwxyz
qwerty
QWERTYUIOP
qwertyuiopasdfg
YYYYYYYDDDDDDDDDDD
ZAAZZ
@@ -32,3 +37,4 @@ ZYXWVUT
ZZBBZ
ZZZBB
ZZZBZ
ZZZZZ

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,6 @@
http
td
www
ecma
rapidtables
WCAG
freedesktop
ycombinator
robertelder
kovidgoyal
leonerd
fixterms
uk
winui
appshellintegration
cppreference
mdtauk
gfycat
what3words
Guake

View 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

View File

@@ -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
@@ -22,3 +17,80 @@ Base64::s_(?:En|De)code\(L"[^"]+"
VERIFY_ARE_EQUAL\(L"[^"]+"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\+/"
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

12
.github/actions/spelling/reject.txt vendored Normal file
View File

@@ -0,0 +1,12 @@
^attache$
^attacher$
^attachers$
benefitting
occurences?
^dependan.*
^oer$
Sorce
^[Ss]pae.*
^untill$
^untilling$
^wether.*

View File

@@ -1,15 +0,0 @@
name: Spell checking
on:
pull_request_target:
push:
jobs:
build:
name: Spell checking
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 5
- uses: check-spelling/check-spelling@0.0.17-alpha

134
.github/workflows/spelling2.yml vendored Normal file
View 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 }}

View File

@@ -106,9 +106,9 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(1u, commands.Size());
auto command = commands.Lookup(L"action0");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
}
{
@@ -117,9 +117,9 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(1u, commands.Size());
auto command = commands.Lookup(L"action0");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::PasteText, command.Action().Action());
VERIFY_IS_NULL(command.Action().Args());
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::PasteText, command.ActionAndArgs().Action());
VERIFY_IS_NULL(command.ActionAndArgs().Args());
}
{
auto warnings = implementation::Command::LayerJson(commands, commands2Json);
@@ -127,9 +127,9 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(1u, commands.Size());
auto command = commands.Lookup(L"action0");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
}
{
@@ -165,9 +165,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"command1");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
@@ -176,9 +176,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"command2");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
@@ -187,9 +187,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"command4");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
@@ -198,9 +198,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"command5");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
@@ -209,9 +209,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"command6");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
@@ -239,9 +239,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"command1");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
@@ -268,9 +268,9 @@ namespace SettingsModelLocalTests
// this test will break.
auto command = commands.Lookup(L"Duplicate tab");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
}
}
@@ -309,9 +309,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"Split pane");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
@@ -319,9 +319,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"Split pane, split: vertical");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
@@ -329,9 +329,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"Split pane, split: horizontal");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
@@ -355,9 +355,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"Split pane");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
@@ -410,9 +410,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"action0");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
@@ -423,9 +423,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"action1");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
@@ -436,9 +436,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"action2");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
@@ -449,9 +449,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"action3");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
@@ -462,9 +462,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"action4");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
@@ -477,9 +477,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"action5");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);
@@ -492,9 +492,9 @@ namespace SettingsModelLocalTests
{
auto command = commands.Lookup(L"action6");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.Action());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
VERIFY_IS_NOT_NULL(realArgs);
const auto& terminalArgs = realArgs.TerminalArgs();
VERIFY_IS_NOT_NULL(terminalArgs);

View File

@@ -1918,7 +1918,12 @@ namespace SettingsModelLocalTests
const auto settingsObject = VerifyParseSucceeded(badSettings);
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
VERIFY_ARE_EQUAL(0u, settings->_globals->_keymap->_keyShortcuts.size());
// KeyMap: ctrl+a/b are mapped to "invalid"
// ActionMap: "splitPane" and "invalid" are the only deserialized actions
// NameMap: "splitPane" has no key binding, but it is still added to the name map
VERIFY_ARE_EQUAL(2u, settings->_globals->_actionMap->_KeyMap.size());
VERIFY_ARE_EQUAL(2u, settings->_globals->_actionMap->_ActionMap.size());
VERIFY_ARE_EQUAL(1u, settings->_globals->_actionMap->NameMap().Size());
VERIFY_ARE_EQUAL(4u, settings->_globals->_keybindingsWarnings.size());
VERIFY_ARE_EQUAL(SettingsLoadWarnings::TooManyKeysForChord, settings->_globals->_keybindingsWarnings.at(0));
@@ -1962,7 +1967,10 @@ namespace SettingsModelLocalTests
auto settings = implementation::CascadiaSettings::FromJson(settingsObject);
VERIFY_ARE_EQUAL(0u, settings->_globals->_keymap->_keyShortcuts.size());
VERIFY_ARE_EQUAL(3u, settings->_globals->_actionMap->_KeyMap.size());
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('a') }));
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('b') }));
VERIFY_IS_NULL(settings->_globals->_actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
for (const auto& warning : settings->_globals->_keybindingsWarnings)
{
@@ -2103,18 +2111,18 @@ namespace SettingsModelLocalTests
const auto profile2Guid = settings->_allProfiles.GetAt(2).Guid();
VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
auto keymap = winrt::get_self<implementation::KeyMapping>(settings->_globals->KeyMap());
VERIFY_ARE_EQUAL(5u, keymap->_keyShortcuts.size());
auto actionMap = winrt::get_self<implementation::ActionMap>(settings->_globals->ActionMap());
VERIFY_ARE_EQUAL(5u, actionMap->_KeyMap.size());
// A/D, B, C, E will be in the list of commands, for 4 total.
// * A and D share the same name, so they'll only generate a single action.
// * F's name is set manually to `null`
auto commands = settings->_globals->Commands();
VERIFY_ARE_EQUAL(4u, commands.Size());
const auto& nameMap{ actionMap->NameMap() };
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2131,7 +2139,7 @@ namespace SettingsModelLocalTests
{
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2145,7 +2153,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2159,7 +2167,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2173,7 +2181,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -2187,11 +2195,21 @@ namespace SettingsModelLocalTests
}
Log::Comment(L"Now verify the commands");
_logCommandNames(commands);
_logCommandNames(nameMap);
{
auto command = commands.Lookup(L"Split pane, split: vertical");
// This was renamed to "ctrl+c" in C. So this does not exist.
auto command = nameMap.TryLookup(L"Split pane, split: vertical");
VERIFY_IS_NULL(command);
}
{
// This was renamed to "ctrl+c" in C. So this does not exist.
auto command = nameMap.TryLookup(L"ctrl+b");
VERIFY_IS_NULL(command);
}
{
auto command = nameMap.TryLookup(L"ctrl+c");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -2205,52 +2223,9 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
}
{
auto command = commands.Lookup(L"ctrl+b");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
}
{
auto command = commands.Lookup(L"ctrl+c");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
}
{
auto command = commands.Lookup(L"Split pane, split: horizontal");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
// This was renamed to null (aka removed from the name map) in F. So this does not exist.
auto command = nameMap.TryLookup(L"Split pane, split: horizontal");
VERIFY_IS_NULL(command);
}
}
@@ -2308,16 +2283,16 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
auto commands = settings->_globals->Commands();
settings->_ValidateSettings();
_logCommandNames(commands);
const auto& nameMap{ settings->ActionMap().NameMap() };
_logCommandNames(nameMap);
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
// Because the "parent" command didn't have a name, it couldn't be
// placed into the list of commands. It and it's children are just
// ignored.
VERIFY_ARE_EQUAL(0u, commands.Size());
VERIFY_ARE_EQUAL(0u, nameMap.Size());
}
void DeserializationTests::TestNestedCommandWithBadSubCommands()
@@ -2358,13 +2333,13 @@ namespace SettingsModelLocalTests
auto settings = winrt::make_self<implementation::CascadiaSettings>();
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
auto commands = settings->_globals->Commands();
settings->_ValidateSettings();
VERIFY_ARE_EQUAL(2u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->_warnings.GetAt(1));
VERIFY_ARE_EQUAL(0u, commands.Size());
const auto& nameMap{ settings->ActionMap().NameMap() };
VERIFY_ARE_EQUAL(0u, nameMap.Size());
}
void DeserializationTests::TestUnbindNestedCommand()
@@ -2432,22 +2407,23 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
auto commands = settings->_globals->Commands();
settings->_ValidateSettings();
_logCommandNames(commands);
auto nameMap{ settings->ActionMap().NameMap() };
_logCommandNames(nameMap);
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, commands.Size());
VERIFY_ARE_EQUAL(1u, nameMap.Size());
Log::Comment(L"Layer second bit of json, to unbind the original command.");
settings->_ParseJsonString(settings1Json, false);
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
commands = settings->_globals->Commands();
_logCommandNames(commands);
nameMap = settings->ActionMap().NameMap();
_logCommandNames(nameMap);
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(0u, commands.Size());
VERIFY_ARE_EQUAL(0u, nameMap.Size());
}
void DeserializationTests::TestRebindNestedCommand()
@@ -2516,16 +2492,17 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
auto commands = settings->_globals->Commands();
const auto& actionMap{ settings->ActionMap() };
settings->_ValidateSettings();
_logCommandNames(commands);
auto nameMap{ actionMap.NameMap() };
_logCommandNames(nameMap);
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, commands.Size());
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
winrt::hstring commandName{ L"parent" };
auto commandProj = commands.Lookup(commandName);
auto commandProj = nameMap.TryLookup(commandName);
VERIFY_IS_NOT_NULL(commandProj);
winrt::com_ptr<implementation::Command> commandImpl;
@@ -2539,17 +2516,18 @@ namespace SettingsModelLocalTests
settings->_ParseJsonString(settings1Json, false);
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
commands = settings->_globals->Commands();
_logCommandNames(commands);
nameMap = settings->ActionMap().NameMap();
_logCommandNames(nameMap);
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, commands.Size());
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
winrt::hstring commandName{ L"parent" };
auto commandProj = commands.Lookup(commandName);
auto commandProj = nameMap.TryLookup(commandName);
VERIFY_IS_NOT_NULL(commandProj);
auto actionAndArgs = commandProj.Action();
auto actionAndArgs = commandProj.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
@@ -2642,8 +2620,10 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(settings->_globals->_colorSchemes.HasKey(schemeName), copyImpl->_globals->_colorSchemes.HasKey(schemeName));
// test actions
VERIFY_ARE_EQUAL(settings->_globals->_keymap->_keyShortcuts.size(), copyImpl->_globals->_keymap->_keyShortcuts.size());
VERIFY_ARE_EQUAL(settings->_globals->_commands.Size(), copyImpl->_globals->_commands.Size());
VERIFY_ARE_EQUAL(settings->_globals->_actionMap->_KeyMap.size(), copyImpl->_globals->_actionMap->_KeyMap.size());
const auto& nameMapOriginal{ settings->_globals->_actionMap->NameMap() };
const auto& nameMapCopy{ copyImpl->_globals->_actionMap->NameMap() };
VERIFY_ARE_EQUAL(nameMapOriginal.Size(), nameMapCopy.Size());
// Test that changing the copy should not change the original
VERIFY_ARE_EQUAL(settings->_globals->WordDelimiters(), copyImpl->_globals->WordDelimiters());

View File

@@ -5,7 +5,7 @@
#include "../TerminalSettingsModel/ColorScheme.h"
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "../TerminalSettingsModel/KeyMapping.h"
#include "../TerminalSettingsModel/ActionMap.h"
#include "JsonTestClass.h"
#include "TestUtils.h"
@@ -51,6 +51,8 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestToggleCommandPaletteArgs);
TEST_METHOD(TestMoveTabArgs);
TEST_METHOD(TestGetKeyBindingForAction);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
@@ -71,18 +73,18 @@ namespace SettingsModelLocalTests
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
keymap->LayerJson(bindings1Json);
VERIFY_ARE_EQUAL(2u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings1Json);
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
keymap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(4u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size());
}
void KeyBindingsTests::LayerKeybindings()
@@ -95,18 +97,18 @@ namespace SettingsModelLocalTests
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
keymap->LayerJson(bindings1Json);
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings1Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
keymap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(2u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
}
void KeyBindingsTests::UnbindKeybindings()
@@ -125,52 +127,57 @@ namespace SettingsModelLocalTests
const auto bindings4Json = VerifyParseSucceeded(bindings4String);
const auto bindings5Json = VerifyParseSucceeded(bindings5String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
keymap->LayerJson(bindings1Json);
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings1Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
Log::Comment(NoThrowString().Format(
L"Try unbinding a key using `\"unbound\"` to unbind the key"));
keymap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
Log::Comment(NoThrowString().Format(
L"Try unbinding a key using `null` to unbind the key"));
// First add back a good binding
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
// Then try layering in the bad setting
keymap->LayerJson(bindings3Json);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings3Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
Log::Comment(NoThrowString().Format(
L"Try unbinding a key using an unrecognized command to unbind the key"));
// First add back a good binding
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
// Then try layering in the bad setting
keymap->LayerJson(bindings4Json);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings4Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
Log::Comment(NoThrowString().Format(
L"Try unbinding a key using a straight up invalid value to unbind the key"));
// First add back a good binding
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
// Then try layering in the bad setting
keymap->LayerJson(bindings5Json);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings5Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
Log::Comment(NoThrowString().Format(
L"Try unbinding a key that wasn't bound at all"));
keymap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
actionMap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ KeyModifiers::Ctrl, static_cast<int32_t>('c') }));
}
void KeyBindingsTests::TestArbitraryArgs()
@@ -194,17 +201,17 @@ namespace SettingsModelLocalTests
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(10u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(10u, actionMap->_KeyMap.size());
{
Log::Comment(NoThrowString().Format(
L"Verify that `copy` without args parses as Copy(SingleLine=false)"));
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
@@ -215,7 +222,7 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Verify that `copy` with args parses them correctly"));
KeyChord kc{ true, false, true, static_cast<int32_t>('C') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
@@ -226,7 +233,7 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Verify that `copy` with args parses them correctly"));
KeyChord kc{ false, true, true, static_cast<int32_t>('C') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
@@ -237,7 +244,7 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Verify that `newTab` without args parses as NewTab(Index=null)"));
KeyChord kc{ true, false, false, static_cast<int32_t>('T') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -249,7 +256,7 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Verify that `newTab` parses args correctly"));
KeyChord kc{ true, false, true, static_cast<int32_t>('T') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -263,7 +270,7 @@ namespace SettingsModelLocalTests
L"Verify that `newTab` with an index greater than the legacy "
L"args afforded parses correctly"));
KeyChord kc{ true, false, true, static_cast<int32_t>('Y') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -277,7 +284,7 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Verify that `copy` ignores args it doesn't understand"));
KeyChord kc{ true, false, true, static_cast<int32_t>('B') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -289,7 +296,7 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Verify that `copy` null as it's `args` parses as the default option"));
KeyChord kc{ true, false, true, static_cast<int32_t>('B') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -301,7 +308,7 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Verify that `adjustFontSize` with a positive delta parses args correctly"));
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -313,7 +320,7 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Verify that `adjustFontSize` with a negative delta parses args correctly"));
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -333,15 +340,15 @@ namespace SettingsModelLocalTests
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(4u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(4u, actionMap->_KeyMap.size());
{
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -350,7 +357,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -359,7 +366,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -368,7 +375,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -387,15 +394,15 @@ namespace SettingsModelLocalTests
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(3u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
{
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -404,7 +411,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -415,7 +422,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -432,15 +439,15 @@ namespace SettingsModelLocalTests
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
{
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
@@ -461,15 +468,15 @@ namespace SettingsModelLocalTests
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(6u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(6u, actionMap->_KeyMap.size());
{
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_UP) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::ScrollUp, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<ScrollUpArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -478,7 +485,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_DOWN) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::ScrollDown, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<ScrollDownArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -487,7 +494,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_UP) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::ScrollUp, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<ScrollUpArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -496,7 +503,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_DOWN) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::ScrollDown, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<ScrollDownArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -505,7 +512,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_UP) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::ScrollUp, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<ScrollUpArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -515,7 +522,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_DOWN) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::ScrollDown, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<ScrollDownArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -526,10 +533,10 @@ namespace SettingsModelLocalTests
{
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "scrollDown", "rowsToScroll": -1 } }])" };
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
auto invalidKeyMap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(invalidKeyMap);
VERIFY_ARE_EQUAL(0u, invalidKeyMap->_keyShortcuts.size());
VERIFY_THROWS(invalidKeyMap->LayerJson(bindingsInvalidJson);, std::exception);
auto invalidActionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(invalidActionMap);
VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size());
VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception);
}
}
@@ -542,15 +549,15 @@ namespace SettingsModelLocalTests
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(2u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
{
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_UP) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::MoveTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<MoveTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -559,7 +566,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_DOWN) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::MoveTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<MoveTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -568,17 +575,17 @@ namespace SettingsModelLocalTests
}
{
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": "moveTab" }])" };
auto keyMapNoArgs = winrt::make_self<implementation::KeyMapping>();
keyMapNoArgs->LayerJson(bindingsInvalidString);
VERIFY_ARE_EQUAL(0u, keyMapNoArgs->_keyShortcuts.size());
auto actionMapNoArgs = winrt::make_self<implementation::ActionMap>();
actionMapNoArgs->LayerJson(bindingsInvalidString);
VERIFY_ARE_EQUAL(0u, actionMapNoArgs->_KeyMap.size());
}
{
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "moveTab", "direction": "bad" } }])" };
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
auto invalidKeyMap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(invalidKeyMap);
VERIFY_ARE_EQUAL(0u, invalidKeyMap->_keyShortcuts.size());
VERIFY_THROWS(invalidKeyMap->LayerJson(bindingsInvalidJson);, std::exception);
auto invalidActionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(invalidActionMap);
VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size());
VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception);
}
}
@@ -592,15 +599,15 @@ namespace SettingsModelLocalTests
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto keymap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(keymap);
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
keymap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(3u, keymap->_keyShortcuts.size());
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
{
KeyChord kc{ false, false, false, static_cast<int32_t>(VK_UP) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::ToggleCommandPalette, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<ToggleCommandPaletteArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -609,7 +616,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>(VK_UP) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::ToggleCommandPalette, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<ToggleCommandPaletteArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -618,7 +625,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, true, static_cast<int32_t>(VK_UP) };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::ToggleCommandPalette, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<ToggleCommandPaletteArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -628,10 +635,69 @@ namespace SettingsModelLocalTests
{
const std::string bindingsInvalidString{ R"([{ "keys": ["up"], "command": { "action": "commandPalette", "launchMode": "bad" } }])" };
const auto bindingsInvalidJson = VerifyParseSucceeded(bindingsInvalidString);
auto invalidKeyMap = winrt::make_self<implementation::KeyMapping>();
VERIFY_IS_NOT_NULL(invalidKeyMap);
VERIFY_ARE_EQUAL(0u, invalidKeyMap->_keyShortcuts.size());
VERIFY_THROWS(invalidKeyMap->LayerJson(bindingsInvalidJson);, std::exception);
auto invalidActionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(invalidActionMap);
VERIFY_ARE_EQUAL(0u, invalidActionMap->_KeyMap.size());
VERIFY_THROWS(invalidActionMap->LayerJson(bindingsInvalidJson);, std::exception);
}
}
void KeyBindingsTests::TestGetKeyBindingForAction()
{
const std::string bindings0String{ R"([ { "command": "closeWindow", "keys": "ctrl+a" } ])" };
const std::string bindings1String{ R"([ { "command": { "action": "copy", "singleLine": true }, "keys": "ctrl+b" } ])" };
const std::string bindings2String{ R"([ { "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+c" } ])" };
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
auto VerifyKeyChordEquality = [](const KeyChord& expected, const KeyChord& actual) {
if (expected)
{
VERIFY_IS_NOT_NULL(actual);
VERIFY_ARE_EQUAL(expected.Modifiers(), actual.Modifiers());
VERIFY_ARE_EQUAL(expected.Vkey(), actual.Vkey());
}
else
{
VERIFY_IS_NULL(actual);
}
};
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_NOT_NULL(actionMap);
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
{
Log::Comment(L"simple command: no args");
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CloseWindow) };
VerifyKeyChordEquality({ KeyModifiers::Ctrl, static_cast<int32_t>('A') }, kbd);
}
{
Log::Comment(L"command with args");
actionMap->LayerJson(bindings1Json);
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
auto args{ winrt::make_self<implementation::CopyTextArgs>() };
args->SingleLine(true);
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::CopyText, *args) };
VerifyKeyChordEquality({ KeyModifiers::Ctrl, static_cast<int32_t>('B') }, kbd);
}
{
Log::Comment(L"command with new terminal args");
actionMap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(3u, actionMap->_KeyMap.size());
auto newTerminalArgs{ winrt::make_self<implementation::NewTerminalArgs>() };
newTerminalArgs->ProfileIndex(0);
auto args{ winrt::make_self<implementation::NewTabArgs>(*newTerminalArgs) };
const auto& kbd{ actionMap->GetKeyBindingForAction(ShortcutAction::NewTab, *args) };
VerifyKeyChordEquality({ KeyModifiers::Ctrl, static_cast<int32_t>('C') }, kbd);
}
}
}

View File

@@ -76,6 +76,13 @@
</ClCompile>
<Link>
<AdditionalDependencies>onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
<!--
SettingsModelLib contains a DllMain that we need to force the use of.
If you don't have this, then you'll see an error like
"(init.obj) : error LNK2005: DllMain already defined in MSVCRTD.lib(dll_dllmain_stub.obj)"
-->
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain</AdditionalOptions>
</Link>
</ItemDefinitionGroup>

View File

@@ -103,17 +103,18 @@ namespace SettingsModelLocalTests
CascadiaSettings settings{ til::u8u16(settingsJson) };
auto keymap = settings.GlobalSettings().KeyMap();
auto actionMap = settings.GlobalSettings().ActionMap();
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
const auto profile2Guid = settings.ActiveProfiles().GetAt(2).Guid();
VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
VERIFY_ARE_EQUAL(12u, keymap.Size());
const auto& actionMapImpl{ winrt::get_self<implementation::ActionMap>(actionMap) };
VERIFY_ARE_EQUAL(12u, actionMapImpl->_KeyMap.size());
{
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -134,7 +135,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('B') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -156,7 +157,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -178,7 +179,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -200,7 +201,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -222,7 +223,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -245,7 +246,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -265,7 +266,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -287,7 +288,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('I') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -310,7 +311,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('J') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -332,7 +333,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('K') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);
@@ -355,7 +356,7 @@ namespace SettingsModelLocalTests
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('L') };
auto actionAndArgs = ::TestUtils::GetActionAndArgs(keymap, kc);
auto actionAndArgs = ::TestUtils::GetActionAndArgs(actionMap, kc);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
VERIFY_IS_NOT_NULL(realArgs);

View File

@@ -19,11 +19,11 @@ public:
// - This is a helper to retrieve the ActionAndArgs from the keybindings
// for a given chord.
// Arguments:
// - keymap: The AppKeyBindings to lookup the ActionAndArgs from.
// - actionMap: The ActionMap to lookup the ActionAndArgs from.
// - kc: The key chord to look up the bound ActionAndArgs for.
// Return Value:
// - The ActionAndArgs bound to the given key, or nullptr if nothing is bound to it.
static const winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs GetActionAndArgs(const winrt::Microsoft::Terminal::Settings::Model::KeyMapping& keymap,
static const winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs GetActionAndArgs(const winrt::Microsoft::Terminal::Settings::Model::ActionMap& actionMap,
const winrt::Microsoft::Terminal::Control::KeyChord& kc)
{
std::wstring buffer{ L"" };
@@ -42,8 +42,8 @@ public:
buffer += static_cast<wchar_t>(MapVirtualKeyW(kc.Vkey(), MAPVK_VK_TO_CHAR));
WEX::Logging::Log::Comment(WEX::Common::NoThrowString().Format(L"Looking for key:%s", buffer.c_str()));
const auto action = keymap.TryLookup(kc);
VERIFY_IS_NOT_NULL(action, L"Expected to find an action bound to the given KeyChord");
return action;
const auto cmd = actionMap.GetActionByKeyChord(kc);
VERIFY_IS_NOT_NULL(cmd, L"Expected to find an action bound to the given KeyChord");
return cmd.ActionAndArgs();
};
};

View File

@@ -120,13 +120,13 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto commands = settings.GlobalSettings().Commands();
VERIFY_ARE_EQUAL(1u, commands.Size());
auto nameMap{ settings.ActionMap().NameMap() };
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
auto command = commands.Lookup(L"iterable command ${profile.name}");
auto command = nameMap.TryLookup(L"iterable command ${profile.name}");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -141,7 +141,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
}
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -150,7 +150,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"iterable command profile0");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -168,7 +168,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"iterable command profile1");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -186,7 +186,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"iterable command profile2");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -247,13 +247,13 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto commands = settings.GlobalSettings().Commands();
VERIFY_ARE_EQUAL(1u, commands.Size());
auto nameMap{ settings.ActionMap().NameMap() };
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
auto command = commands.Lookup(L"Split pane, profile: ${profile.name}");
auto command = nameMap.TryLookup(L"Split pane, profile: ${profile.name}");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -268,7 +268,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
}
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -277,7 +277,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"Split pane, profile: profile0");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -295,7 +295,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"Split pane, profile: profile1");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -313,7 +313,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"Split pane, profile: profile2");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -376,13 +376,13 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto commands = settings.GlobalSettings().Commands();
VERIFY_ARE_EQUAL(1u, commands.Size());
auto nameMap{ settings.ActionMap().NameMap() };
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
auto command = commands.Lookup(L"iterable command ${profile.name}");
auto command = nameMap.TryLookup(L"iterable command ${profile.name}");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -397,7 +397,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
}
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -406,7 +406,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"iterable command profile0");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -424,7 +424,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"iterable command profile1\"");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -442,7 +442,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"iterable command profile2");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -513,8 +513,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto commands = settings.GlobalSettings().Commands();
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -522,8 +521,9 @@ namespace TerminalAppLocalTests
auto rootCommand = expandedCommands.Lookup(L"Connect to ssh...");
VERIFY_IS_NOT_NULL(rootCommand);
auto rootActionAndArgs = rootCommand.Action();
VERIFY_IS_NULL(rootActionAndArgs);
auto rootActionAndArgs = rootCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(rootActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
VERIFY_ARE_EQUAL(2u, rootCommand.NestedCommands().Size());
@@ -531,7 +531,7 @@ namespace TerminalAppLocalTests
winrt::hstring commandName{ L"first.com" };
auto command = rootCommand.NestedCommands().Lookup(commandName);
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_IS_FALSE(command.HasNestedCommands());
@@ -540,7 +540,7 @@ namespace TerminalAppLocalTests
winrt::hstring commandName{ L"second.com" };
auto command = rootCommand.NestedCommands().Lookup(commandName);
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_IS_FALSE(command.HasNestedCommands());
@@ -608,8 +608,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto commands = settings.GlobalSettings().Commands();
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -617,23 +616,25 @@ namespace TerminalAppLocalTests
auto grandparentCommand = expandedCommands.Lookup(L"grandparent");
VERIFY_IS_NOT_NULL(grandparentCommand);
auto grandparentActionAndArgs = grandparentCommand.Action();
VERIFY_IS_NULL(grandparentActionAndArgs);
auto grandparentActionAndArgs = grandparentCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(grandparentActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, grandparentActionAndArgs.Action());
VERIFY_ARE_EQUAL(1u, grandparentCommand.NestedCommands().Size());
winrt::hstring parentName{ L"parent" };
auto parent = grandparentCommand.NestedCommands().Lookup(parentName);
VERIFY_IS_NOT_NULL(parent);
auto parentActionAndArgs = parent.Action();
VERIFY_IS_NULL(parentActionAndArgs);
auto parentActionAndArgs = parent.ActionAndArgs();
VERIFY_IS_NOT_NULL(parentActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, parentActionAndArgs.Action());
VERIFY_ARE_EQUAL(2u, parent.NestedCommands().Size());
{
winrt::hstring childName{ L"child1" };
auto child = parent.NestedCommands().Lookup(childName);
VERIFY_IS_NOT_NULL(child);
auto childActionAndArgs = child.Action();
auto childActionAndArgs = child.ActionAndArgs();
VERIFY_IS_NOT_NULL(childActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, childActionAndArgs.Action());
@@ -653,7 +654,7 @@ namespace TerminalAppLocalTests
winrt::hstring childName{ L"child2" };
auto child = parent.NestedCommands().Lookup(childName);
VERIFY_IS_NOT_NULL(child);
auto childActionAndArgs = child.Action();
auto childActionAndArgs = child.ActionAndArgs();
VERIFY_IS_NOT_NULL(childActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, childActionAndArgs.Action());
@@ -731,8 +732,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto commands = settings.GlobalSettings().Commands();
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -744,8 +744,9 @@ namespace TerminalAppLocalTests
winrt::hstring commandName{ name + L"..." };
auto command = expandedCommands.Lookup(commandName);
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
VERIFY_IS_NULL(actionAndArgs);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, actionAndArgs.Action());
VERIFY_IS_TRUE(command.HasNestedCommands());
VERIFY_ARE_EQUAL(3u, command.NestedCommands().Size());
@@ -754,7 +755,7 @@ namespace TerminalAppLocalTests
winrt::hstring childCommandName{ fmt::format(L"Split pane, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.Action();
auto childActionAndArgs = childCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(childActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
@@ -775,7 +776,7 @@ namespace TerminalAppLocalTests
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: horizontal, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.Action();
auto childActionAndArgs = childCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(childActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
@@ -796,7 +797,7 @@ namespace TerminalAppLocalTests
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: vertical, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.Action();
auto childActionAndArgs = childCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(childActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
@@ -868,8 +869,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto commands = settings.GlobalSettings().Commands();
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -877,8 +877,9 @@ namespace TerminalAppLocalTests
auto rootCommand = expandedCommands.Lookup(L"New Tab With Profile...");
VERIFY_IS_NOT_NULL(rootCommand);
auto rootActionAndArgs = rootCommand.Action();
VERIFY_IS_NULL(rootActionAndArgs);
auto rootActionAndArgs = rootCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(rootActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
VERIFY_ARE_EQUAL(3u, rootCommand.NestedCommands().Size());
@@ -887,7 +888,7 @@ namespace TerminalAppLocalTests
winrt::hstring commandName{ fmt::format(L"New tab, profile: {}", name) };
auto command = rootCommand.NestedCommands().Lookup(commandName);
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
@@ -971,8 +972,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto commands = settings.GlobalSettings().Commands();
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
@@ -980,8 +980,9 @@ namespace TerminalAppLocalTests
auto rootCommand = expandedCommands.Lookup(L"New Pane...");
VERIFY_IS_NOT_NULL(rootCommand);
auto rootActionAndArgs = rootCommand.Action();
VERIFY_IS_NULL(rootActionAndArgs);
auto rootActionAndArgs = rootCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(rootActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
VERIFY_ARE_EQUAL(3u, rootCommand.NestedCommands().Size());
@@ -990,8 +991,9 @@ namespace TerminalAppLocalTests
winrt::hstring commandName{ name + L"..." };
auto command = rootCommand.NestedCommands().Lookup(commandName);
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
VERIFY_IS_NULL(actionAndArgs);
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, actionAndArgs.Action());
VERIFY_IS_TRUE(command.HasNestedCommands());
VERIFY_ARE_EQUAL(3u, command.NestedCommands().Size());
@@ -1001,7 +1003,7 @@ namespace TerminalAppLocalTests
winrt::hstring childCommandName{ fmt::format(L"Split pane, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.Action();
auto childActionAndArgs = childCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(childActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
@@ -1022,7 +1024,7 @@ namespace TerminalAppLocalTests
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: horizontal, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.Action();
auto childActionAndArgs = childCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(childActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
@@ -1043,7 +1045,7 @@ namespace TerminalAppLocalTests
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: vertical, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.Action();
auto childActionAndArgs = childCommand.ActionAndArgs();
VERIFY_IS_NOT_NULL(childActionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, childActionAndArgs.Action());
@@ -1113,13 +1115,13 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
auto commands = settings.GlobalSettings().Commands();
VERIFY_ARE_EQUAL(1u, commands.Size());
auto nameMap{ settings.ActionMap().NameMap() };
VERIFY_ARE_EQUAL(1u, nameMap.Size());
{
auto command = commands.Lookup(L"iterable command ${scheme.name}");
auto command = nameMap.TryLookup(L"iterable command ${scheme.name}");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -1134,7 +1136,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"${scheme.name}", realArgs.TerminalArgs().Profile());
}
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(commands, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
_logCommandNames(expandedCommands.GetView());
// This is the same warning as above
@@ -1148,7 +1150,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"iterable command scheme_0");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -1166,7 +1168,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"iterable command scheme_1");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
@@ -1184,7 +1186,7 @@ namespace TerminalAppLocalTests
{
auto command = expandedCommands.Lookup(L"iterable command scheme_2");
VERIFY_IS_NOT_NULL(command);
auto actionAndArgs = command.Action();
auto actionAndArgs = command.ActionAndArgs();
VERIFY_IS_NOT_NULL(actionAndArgs);
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();

View File

@@ -42,11 +42,11 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::_EndPreview()
{
if (_lastPreviewedCommand == nullptr || _lastPreviewedCommand.Action() == nullptr)
if (_lastPreviewedCommand == nullptr || _lastPreviewedCommand.ActionAndArgs() == nullptr)
{
return;
}
switch (_lastPreviewedCommand.Action().Action())
switch (_lastPreviewedCommand.ActionAndArgs().Action())
{
case ShortcutAction::SetColorScheme:
{
@@ -160,17 +160,17 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_PreviewActionHandler(const IInspectable& /*sender*/,
const Microsoft::Terminal::Settings::Model::Command& args)
{
if (args == nullptr || args.Action() == nullptr)
if (args == nullptr || args.ActionAndArgs() == nullptr)
{
_EndPreview();
}
else
{
switch (args.Action().Action())
switch (args.ActionAndArgs().Action())
{
case ShortcutAction::SetColorScheme:
{
_PreviewColorScheme(args.Action().Args().try_as<SetColorSchemeArgs>());
_PreviewColorScheme(args.ActionAndArgs().Args().try_as<SetColorSchemeArgs>());
break;
}
}

View File

@@ -14,10 +14,9 @@ namespace winrt::TerminalApp::implementation
{
bool AppKeyBindings::TryKeyChord(const KeyChord& kc)
{
const auto actionAndArgs = _keymap.TryLookup(kc);
if (actionAndArgs)
if (const auto cmd{ _actionMap.GetActionByKeyChord(kc) })
{
return _dispatch.DoAction(actionAndArgs);
return _dispatch.DoAction(cmd.ActionAndArgs());
}
return false;
}
@@ -27,8 +26,8 @@ namespace winrt::TerminalApp::implementation
_dispatch = dispatch;
}
void AppKeyBindings::SetKeyMapping(const winrt::Microsoft::Terminal::Settings::Model::KeyMapping& keymap)
void AppKeyBindings::SetActionMap(const winrt::Microsoft::Terminal::Settings::Model::ActionMap& actionMap)
{
_keymap = keymap;
_actionMap = actionMap;
}
}

View File

@@ -22,10 +22,10 @@ namespace winrt::TerminalApp::implementation
bool TryKeyChord(winrt::Microsoft::Terminal::Control::KeyChord const& kc);
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
void SetKeyMapping(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap);
void SetActionMap(const Microsoft::Terminal::Settings::Model::ActionMap& actionMap);
private:
winrt::Microsoft::Terminal::Settings::Model::KeyMapping _keymap{ nullptr };
winrt::Microsoft::Terminal::Settings::Model::ActionMap _actionMap{ nullptr };
winrt::TerminalApp::ShortcutActionDispatch _dispatch{ nullptr };

View File

@@ -9,6 +9,6 @@ namespace TerminalApp
AppKeyBindings();
void SetDispatch(ShortcutActionDispatch dispatch);
void SetKeyMapping(Microsoft.Terminal.Settings.Model.KeyMapping keymap);
void SetActionMap(Microsoft.Terminal.Settings.Model.ActionMap actionMap);
}
}

View File

@@ -261,19 +261,18 @@ namespace winrt::TerminalApp::implementation
// Only give anchored tab switcher the ability to cycle through tabs with the tab button.
// For unanchored mode, accessibility becomes an issue when we try to hijack tab since it's
// a really widely used keyboard navigation key.
if (_currentMode == CommandPaletteMode::TabSwitchMode && _keymap)
if (_currentMode == CommandPaletteMode::TabSwitchMode && _actionMap)
{
winrt::Microsoft::Terminal::Control::KeyChord kc{ ctrlDown, altDown, shiftDown, static_cast<int32_t>(key) };
const auto action = _keymap.TryLookup(kc);
if (action)
if (const auto cmd{ _actionMap.GetActionByKeyChord(kc) })
{
if (action.Action() == ShortcutAction::PrevTab)
if (cmd.ActionAndArgs().Action() == ShortcutAction::PrevTab)
{
SelectNextItem(false);
e.Handled(true);
return;
}
else if (action.Action() == ShortcutAction::NextTab)
else if (cmd.ActionAndArgs().Action() == ShortcutAction::NextTab)
{
SelectNextItem(true);
e.Handled(true);
@@ -864,9 +863,9 @@ namespace winrt::TerminalApp::implementation
return _filteredActions;
}
void CommandPalette::SetKeyMap(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap)
void CommandPalette::SetActionMap(const Microsoft::Terminal::Settings::Model::ActionMap& actionMap)
{
_keymap = keymap;
_actionMap = actionMap;
}
void CommandPalette::SetCommands(Collections::IVector<Command> const& actions)

View File

@@ -32,7 +32,7 @@ namespace winrt::TerminalApp::implementation
void SetCommands(Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> const& actions);
void SetTabs(Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::TabBase> const& tabs, Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::TabBase> const& mruTabs);
void SetKeyMap(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap);
void SetActionMap(const Microsoft::Terminal::Settings::Model::ActionMap& actionMap);
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
@@ -107,7 +107,7 @@ namespace winrt::TerminalApp::implementation
std::wstring _getTrimmedInput();
void _evaluatePrefix();
Microsoft::Terminal::Settings::Model::KeyMapping _keymap{ nullptr };
Microsoft::Terminal::Settings::Model::ActionMap _actionMap{ nullptr };
// Tab Switcher
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _tabActions{ nullptr };

View File

@@ -25,7 +25,7 @@ namespace TerminalApp
void SetTabs(Windows.Foundation.Collections.IObservableVector<TabBase> tabs, Windows.Foundation.Collections.IObservableVector<TabBase> mruTabs);
void SetKeyMap(Microsoft.Terminal.Settings.Model.KeyMapping keymap);
void SetActionMap(Microsoft.Terminal.Settings.Model.ActionMap actionMap);
void SelectNextItem(Boolean moveDown);

View File

@@ -148,9 +148,9 @@ namespace winrt::TerminalApp::implementation
_dispatch = dispatch;
}
void TabBase::SetKeyMap(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap)
void TabBase::SetActionMap(const Microsoft::Terminal::Settings::Model::ActionMap& actionMap)
{
_keymap = keymap;
_actionMap = actionMap;
_UpdateSwitchToTabKeyChord();
}
@@ -163,9 +163,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
winrt::fire_and_forget TabBase::_UpdateSwitchToTabKeyChord()
{
SwitchToTabArgs args{ _TabViewIndex };
ActionAndArgs switchToTab{ ShortcutAction::SwitchToTab, args };
const auto keyChord = _keymap ? _keymap.GetKeyBindingForActionWithArgs(switchToTab) : nullptr;
const auto keyChord = _actionMap ? _actionMap.GetKeyBindingForAction(ShortcutAction::SwitchToTab, SwitchToTabArgs{ _TabViewIndex }) : nullptr;
const auto keyChordText = keyChord ? KeyChordSerialization::ToString(keyChord) : L"";
if (_keyChord == keyChordText)

View File

@@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs);
void SetKeyMap(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap);
void SetActionMap(const Microsoft::Terminal::Settings::Model::ActionMap& actionMap);
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
WINRT_CALLBACK(CloseRequested, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
@@ -46,7 +46,7 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeOtherTabsMenuItem{};
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeTabsAfterMenuItem{};
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
Microsoft::Terminal::Settings::Model::KeyMapping _keymap{ nullptr };
Microsoft::Terminal::Settings::Model::ActionMap _actionMap{ nullptr };
winrt::hstring _keyChord{};
virtual void _CreateContextMenu();

View File

@@ -130,7 +130,7 @@ namespace winrt::TerminalApp::implementation
_mruTabs.Append(*newTabImpl);
newTabImpl->SetDispatch(*_actionDispatch);
newTabImpl->SetKeyMap(_settings.KeyMap());
newTabImpl->SetActionMap(_settings.ActionMap());
// Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command.
_UpdateTabIndices();

View File

@@ -75,21 +75,19 @@ namespace winrt::TerminalApp::implementation
for (const auto& nameAndCmd : commands)
{
const auto& command = nameAndCmd.Value();
// If there's a keybinding that's bound to exactly this command,
// then get the string for that keychord and display it as a
// part of the command in the UI. Each Command's KeyChordText is
// unset by default, so we don't need to worry about clearing it
// if there isn't a key associated with it.
auto keyChord{ settings.KeyMap().GetKeyBindingForActionWithArgs(command.Action()) };
if (keyChord)
{
command.KeyChordText(KeyChordSerialization::ToString(keyChord));
}
if (command.HasNestedCommands())
{
_recursiveUpdateCommandKeybindingLabels(settings, command.NestedCommands());
}
else
{
// If there's a keybinding that's bound to exactly this command,
// then get the keychord and display it as a
// part of the command in the UI.
// We specifically need to do this for nested commands.
const auto keyChord{ settings.ActionMap().GetKeyBindingForAction(command.ActionAndArgs().Action(), command.ActionAndArgs().Args()) };
command.RegisterKey(keyChord);
}
}
}
@@ -107,7 +105,7 @@ namespace winrt::TerminalApp::implementation
// happen before the Settings UI is reloaded and tries to re-read
// those values.
_UpdateCommandsForPalette();
CommandPalette().SetKeyMap(_settings.KeyMap());
CommandPalette().SetActionMap(_settings.ActionMap());
if (needRefreshUI)
{
@@ -123,7 +121,7 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::Create()
{
// Hookup the key bindings
_HookupKeyBindings(_settings.KeyMap());
_HookupKeyBindings(_settings.ActionMap());
_tabContent = this->TabContent();
_tabRow = this->TabRow();
@@ -276,7 +274,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::_OnDispatchCommandRequested(const IInspectable& /*sender*/, const Microsoft::Terminal::Settings::Model::Command& command)
{
const auto& actionAndArgs = command.Action();
const auto& actionAndArgs = command.ActionAndArgs();
_actionDispatch->DoAction(actionAndArgs);
}
@@ -523,7 +521,7 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_CreateNewTabFlyout()
{
auto newTabFlyout = WUX::Controls::MenuFlyout{};
auto keyBindings = _settings.KeyMap();
auto actionMap = _settings.ActionMap();
const auto defaultProfileGuid = _settings.GlobalSettings().DefaultProfile();
// the number of profiles should not change in the loop for this to work
@@ -538,8 +536,7 @@ namespace winrt::TerminalApp::implementation
// NewTab(ProfileIndex=N) action
NewTerminalArgs newTerminalArgs{ profileIndex };
NewTabArgs newTabArgs{ newTerminalArgs };
ActionAndArgs actionAndArgs{ ShortcutAction::NewTab, newTabArgs };
auto profileKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(actionAndArgs) };
auto profileKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::NewTab, newTabArgs) };
// make sure we find one to display
if (profileKeyChord)
@@ -663,9 +660,7 @@ namespace winrt::TerminalApp::implementation
settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
newTabFlyout.Items().Append(settingsItem);
Microsoft::Terminal::Settings::Model::OpenSettingsArgs args{ SettingsTarget::SettingsUI };
Microsoft::Terminal::Settings::Model::ActionAndArgs settingsAction{ ShortcutAction::OpenSettings, args };
const auto settingsKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(settingsAction) };
const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::OpenSettings, OpenSettingsArgs{ SettingsTarget::SettingsUI }) };
if (settingsKeyChord)
{
_SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
@@ -892,14 +887,13 @@ namespace winrt::TerminalApp::implementation
auto const shiftDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
winrt::Microsoft::Terminal::Control::KeyChord kc{ ctrlDown, altDown, shiftDown, static_cast<int32_t>(key) };
const auto actionAndArgs = _settings.KeyMap().TryLookup(kc);
if (actionAndArgs)
if (const auto cmd{ _settings.ActionMap().GetActionByKeyChord(kc) })
{
if (CommandPalette().Visibility() == Visibility::Visible && actionAndArgs.Action() != ShortcutAction::ToggleCommandPalette)
if (CommandPalette().Visibility() == Visibility::Visible && cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
{
CommandPalette().Visibility(Visibility::Collapsed);
}
_actionDispatch->DoAction(actionAndArgs);
_actionDispatch->DoAction(cmd.ActionAndArgs());
e.Handled(true);
}
}
@@ -920,23 +914,23 @@ namespace winrt::TerminalApp::implementation
auto const shiftDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
winrt::Microsoft::Terminal::Control::KeyChord kc{ ctrlDown, altDown, shiftDown, static_cast<int32_t>(key) };
const auto actionAndArgs = _settings.KeyMap().TryLookup(kc);
if (actionAndArgs && (actionAndArgs.Action() == ShortcutAction::CloseTab || actionAndArgs.Action() == ShortcutAction::NextTab || actionAndArgs.Action() == ShortcutAction::PrevTab || actionAndArgs.Action() == ShortcutAction::ClosePane))
const auto cmd{ _settings.ActionMap().GetActionByKeyChord(kc) };
if (cmd && (cmd.ActionAndArgs().Action() == ShortcutAction::CloseTab || cmd.ActionAndArgs().Action() == ShortcutAction::NextTab || cmd.ActionAndArgs().Action() == ShortcutAction::PrevTab || cmd.ActionAndArgs().Action() == ShortcutAction::ClosePane))
{
_actionDispatch->DoAction(actionAndArgs);
_actionDispatch->DoAction(cmd.ActionAndArgs());
e.Handled(true);
}
}
// Method Description:
// - Configure the AppKeyBindings to use our ShortcutActionDispatch and the updated KeyMapping
// as the object to handle dispatching ShortcutAction events.
// - Configure the AppKeyBindings to use our ShortcutActionDispatch and the updated ActionMap
// as the object to handle dispatching ShortcutAction events.
// Arguments:
// - bindings: A AppKeyBindings object to wire up with our event handlers
void TerminalPage::_HookupKeyBindings(const KeyMapping& keymap) noexcept
// - bindings: An ActionMap object to wire up with our event handlers
void TerminalPage::_HookupKeyBindings(const ActionMap& actionMap) noexcept
{
_bindings->SetDispatch(*_actionDispatch);
_bindings->SetKeyMapping(keymap);
_bindings->SetActionMap(actionMap);
}
// Method Description:
@@ -1388,7 +1382,7 @@ namespace winrt::TerminalApp::implementation
menuShortcut.Key(static_cast<Windows::System::VirtualKey>(keyChord.Vkey()));
// inspect the modifiers from the KeyChord and set the flags int he XAML value
auto modifiers = AppKeyBindings::ConvertVKModifiers(keyChord.Modifiers());
auto modifiers = ActionMap::ConvertVKModifiers(keyChord.Modifiers());
// add the modifiers to the shortcut
menuShortcut.Modifiers(modifiers);
@@ -1812,7 +1806,7 @@ namespace winrt::TerminalApp::implementation
{
// Re-wire the keybindings to their handlers, as we'll have created a
// new AppKeyBindings object.
_HookupKeyBindings(_settings.KeyMap());
_HookupKeyBindings(_settings.ActionMap());
// Refresh UI elements
auto profiles = _settings.ActiveProfiles();
@@ -1860,7 +1854,7 @@ namespace winrt::TerminalApp::implementation
}
auto tabImpl{ winrt::get_self<TabBase>(tab) };
tabImpl->SetKeyMap(_settings.KeyMap());
tabImpl->SetActionMap(_settings.ActionMap());
}
auto weakThis{ get_weak() };
@@ -1941,7 +1935,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::_UpdateCommandsForPalette()
{
IMap<winrt::hstring, Command> copyOfCommands = _ExpandCommands(_settings.GlobalSettings().Commands(),
IMap<winrt::hstring, Command> copyOfCommands = _ExpandCommands(_settings.GlobalSettings().ActionMap().NameMap(),
_settings.ActiveProfiles().GetView(),
_settings.GlobalSettings().ColorSchemes());
@@ -2335,7 +2329,7 @@ namespace winrt::TerminalApp::implementation
_mruTabs.Append(*newTabImpl);
newTabImpl->SetDispatch(*_actionDispatch);
newTabImpl->SetKeyMap(_settings.KeyMap());
newTabImpl->SetActionMap(_settings.ActionMap());
// Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command.
_UpdateTabIndices();

View File

@@ -198,7 +198,7 @@ namespace winrt::TerminalApp::implementation
void _KeyDownHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
void _SUIPreviewKeyDownHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
void _HookupKeyBindings(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap) noexcept;
void _HookupKeyBindings(const Microsoft::Terminal::Settings::Model::ActionMap& actionMap) noexcept;
void _RegisterActionCallbacks();
void _UpdateTitle(const TerminalTab& tab);

View File

@@ -5,11 +5,13 @@
#include "Actions.h"
#include "Actions.g.cpp"
#include "ActionsPageNavigationState.g.cpp"
#include "EnumEntry.h"
#include "KeyBindingContainer.h"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;
using namespace winrt::Microsoft::Terminal::Settings::Model;
@@ -18,44 +20,31 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Actions::Actions()
{
InitializeComponent();
_filteredActions = winrt::single_threaded_observable_vector<winrt::Microsoft::Terminal::Settings::Model::Command>();
}
void Actions::OnNavigatedTo(const NavigationEventArgs& e)
{
_State = e.Parameter().as<Editor::ActionsPageNavigationState>();
for (const auto& [k, command] : _State.Settings().GlobalSettings().Commands())
std::vector<Command> keyBindingList;
for (const auto& [_, command] : _State.Settings().GlobalSettings().ActionMap().NameMap())
{
// Filter out nested commands, and commands that aren't bound to a
// key. This page is currently just for displaying the actions that
// _are_ bound to keys.
if (command.HasNestedCommands() || command.KeyChordText().empty())
if (command.HasNestedCommands() || !command.Keys())
{
continue;
}
_filteredActions.Append(command);
keyBindingList.push_back(command);
}
std::sort(begin(keyBindingList), end(keyBindingList), CommandComparator{});
for (const auto& cmd : keyBindingList)
{
//const auto& container{ make<KeyBindingContainer>(cmd) };
const auto& container{ make<CommandViewModel>(cmd) };
KeyBindingListView().Items().Append(container);
}
}
Collections::IObservableVector<Command> Actions::FilteredActions()
{
return _filteredActions;
}
void Actions::_OpenSettingsClick(const IInspectable& /*sender*/,
const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
{
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile;
_State.RequestOpenJson(target);
}
}

View File

@@ -9,19 +9,21 @@
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct CommandComparator
{
bool operator()(const Model::Command& lhs, const Model::Command& rhs) const
{
return lhs.Name() < rhs.Name();
}
};
struct ActionsPageNavigationState : ActionsPageNavigationStateT<ActionsPageNavigationState>
{
public:
ActionsPageNavigationState(const Model::CascadiaSettings& settings) :
_Settings{ settings } {}
void RequestOpenJson(const Model::SettingsTarget target)
{
_OpenJsonHandlers(nullptr, target);
}
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr)
TYPED_EVENT(OpenJson, Windows::Foundation::IInspectable, Model::SettingsTarget);
};
struct Actions : ActionsT<Actions>
@@ -31,15 +33,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Command> FilteredActions();
WINRT_PROPERTY(Editor::ActionsPageNavigationState, State, nullptr);
private:
friend struct ActionsT<Actions>; // for Xaml to bind events
Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Command> _filteredActions{ nullptr };
void _OpenSettingsClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
};
}

View File

@@ -8,16 +8,11 @@ namespace Microsoft.Terminal.Settings.Editor
runtimeclass ActionsPageNavigationState
{
Microsoft.Terminal.Settings.Model.CascadiaSettings Settings;
void RequestOpenJson(Microsoft.Terminal.Settings.Model.SettingsTarget target);
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.SettingsTarget> OpenJson;
};
[default_interface] runtimeclass Actions : Windows.UI.Xaml.Controls.Page
{
Actions();
ActionsPageNavigationState State { get; };
IObservableVector<Microsoft.Terminal.Settings.Model.Command> FilteredActions { get; };
}
}

View File

@@ -5,11 +5,8 @@
<Page x:Class="Microsoft.Terminal.Settings.Editor.Actions"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d">
<Page.Resources>
@@ -18,210 +15,20 @@
<ResourceDictionary Source="CommonResources.xaml" />
</ResourceDictionary.MergedDictionaries>
<local:StringIsEmptyConverter x:Key="CommandKeyChordVisibilityConverter" />
<!--
Template for actions. This is _heavily_ copied from the command
palette, with modifications:
* We don't need to use a HighlightedTextControl, because we're
not filtering this list
* We don't need the chevron for nested commands
* We're not displaying the icon
* We're binding directly to a Command, not a FilteredCommand
If we wanted to reuse the command palette's list more directly,
that's theoretically possible, but then it would need to be
lifted out of TerminalApp and either moved into the
TerminalSettingsEditor or moved to it's own project consumed by
both TSE and TerminalApp.
-->
<DataTemplate x:Key="GeneralItemTemplate"
x:DataType="SettingsModel:Command">
<!--
This HorizontalContentAlignment="Stretch" is important
to make sure it takes the entire width of the line
-->
<ListViewItem HorizontalContentAlignment="Stretch"
AutomationProperties.AcceleratorKey="{x:Bind KeyChordText, Mode=OneWay}"
AutomationProperties.Name="{x:Bind Name, Mode=OneWay}">
<Grid HorizontalAlignment="Stretch"
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<!-- command label -->
<ColumnDefinition Width="*" />
<!-- key chord -->
<ColumnDefinition Width="32" />
<!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
HorizontalAlignment="Left"
Text="{x:Bind Name, Mode=OneWay}" />
<!--
The block for the key chord is only visible
when there's actual text set as the label. See
CommandKeyChordVisibilityConverter for details.
Inexplicably, we don't need to set the
AutomationProperties to Raw here, unlike in the
CommandPalette. We're not quite sure why.
-->
<Border Grid.Column="1"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Style="{ThemeResource KeyChordBorderStyle}"
Visibility="{x:Bind KeyChordText, Mode=OneWay, Converter={StaticResource CommandKeyChordVisibilityConverter}}">
<TextBlock FontSize="12"
Style="{ThemeResource KeyChordTextBlockStyle}"
Text="{x:Bind KeyChordText, Mode=OneWay}" />
</Border>
</Grid>
</ListViewItem>
</DataTemplate>
<!-- These resources again, HEAVILY copied from the command palette -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<!-- TextBox colors ! -->
<SolidColorBrush x:Key="TextControlBackground"
Color="#333333" />
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush"
Color="#B5B5B5" />
<SolidColorBrush x:Key="TextControlForeground"
Color="#B5B5B5" />
<SolidColorBrush x:Key="TextControlBorderBrush"
Color="#404040" />
<SolidColorBrush x:Key="TextControlButtonForeground"
Color="#B5B5B5" />
<SolidColorBrush x:Key="TextControlBackgroundPointerOver"
Color="#404040" />
<SolidColorBrush x:Key="TextControlForegroundPointerOver"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver"
Color="#404040" />
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver"
Color="#FF4343" />
<SolidColorBrush x:Key="TextControlBackgroundFocused"
Color="#333333" />
<SolidColorBrush x:Key="TextControlForegroundFocused"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlBorderBrushFocused"
Color="#404040" />
<SolidColorBrush x:Key="TextControlButtonForegroundPressed"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed"
Color="#FF4343" />
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<!-- TextBox colors ! -->
<SolidColorBrush x:Key="TextControlBackground"
Color="#CCCCCC" />
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush"
Color="#636363" />
<SolidColorBrush x:Key="TextControlBorderBrush"
Color="#636363" />
<SolidColorBrush x:Key="TextControlButtonForeground"
Color="#636363" />
<SolidColorBrush x:Key="TextControlBackgroundPointerOver"
Color="#DADADA" />
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver"
Color="#636363" />
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver"
Color="#FF4343" />
<SolidColorBrush x:Key="TextControlBackgroundFocused"
Color="#CCCCCC" />
<SolidColorBrush x:Key="TextControlBorderBrushFocused"
Color="#636363" />
<SolidColorBrush x:Key="TextControlButtonForegroundPressed"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed"
Color="#FF4343" />
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<!-- KeyChordText styles (use XAML defaults for High Contrast theme) -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border" />
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Page.Resources>
<ScrollViewer>
<StackPanel Style="{StaticResource SettingsStackStyle}">
<TextBlock x:Uid="Globals_KeybindingsDisclaimer"
Style="{StaticResource DisclaimerStyle}" />
<!--
The Nav_OpenJSON resource just so happens to have a .Content
and .Tooltip that are _exactly_ what we're looking for here.
-->
<HyperlinkButton x:Uid="Nav_OpenJSON"
Click="_OpenSettingsClick" />
<StackPanel HorizontalAlignment="Stretch"
Style="{StaticResource SettingsStackStyle}">
<!-- Keybindings -->
<!--
NOTE: Globals_Keybindings.Header is not defined, because that
would result in the page having "Keybindings" displayed twice, which
looks quite redundant
-->
<ContentPresenter x:Uid="Globals_Keybindings"
Margin="0">
<ListView HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AllowDrop="False"
CanReorderItems="False"
IsItemClickEnabled="False"
ItemTemplate="{StaticResource GeneralItemTemplate}"
ItemsSource="{x:Bind FilteredActions, Mode=OneWay}"
SelectionMode="None" />
</ContentPresenter>
<ListView x:Name="KeyBindingListView"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AllowDrop="False"
CanReorderItems="False"
IsItemClickEnabled="False"
SelectionMode="None" />
</StackPanel>

View File

@@ -29,7 +29,7 @@
<Style x:Key="SettingsStackStyle"
TargetType="StackPanel">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Margin" Value="13,0,0,48" />
<Setter Property="Margin" Value="13,0,13,48" />
</Style>
<!-- Used to stack a group of settings inside a pivot -->

View File

@@ -58,5 +58,4 @@ namespace Microsoft.Terminal.Settings.Editor
{
DesktopWallpaperToEmptyStringConverter();
};
}

View File

@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "KeyBindingContainer.h"
#include "KeyBindingContainer.g.cpp"
//#include "LibraryResources.h"
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Windows::UI::Xaml;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
KeyBindingContainer::KeyBindingContainer(Command cmd) :
_Action{ cmd }
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,37 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- KeyBindingContainer
Abstract:
- This is a XAML data template to represent a key binding as a list view item.
Author(s):
- Carlos Zamora - April 2021
--*/
#pragma once
#include "KeyBindingContainer.g.h"
#include "Utils.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct KeyBindingContainer : KeyBindingContainerT<KeyBindingContainer>
{
public:
KeyBindingContainer() = delete;
KeyBindingContainer(Model::Command cmd);
WINRT_PROPERTY(Model::Command, Action, nullptr);
//TYPED_EVENT(Unbind, Editor::KeyBindingContainer, Windows::Foundation::IInspectable);
};
}
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(KeyBindingContainer);
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Settings.Editor
{
[default_interface] runtimeclass KeyBindingContainer : Windows.UI.Xaml.Controls.ListViewItem
{
KeyBindingContainer(Microsoft.Terminal.Settings.Model.Command cmd);
Microsoft.Terminal.Settings.Model.Command Action { get; };
//event Windows.Foundation.TypedEventHandler<KeyBindingContainer, Object> Unbind;
};
}

View File

@@ -0,0 +1,238 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
-->
<ListViewItem x:Class="Microsoft.Terminal.Settings.Editor.KeyBindingContainer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:model="using:Microsoft.Terminal.Settings.Model"
HorizontalContentAlignment="Stretch"
AutomationProperties.AcceleratorKey="{x:Bind Action.KeyChordText, Mode=OneWay}"
AutomationProperties.Name="{x:Bind Action.Name, Mode=OneWay}"
mc:Ignorable="d">
<ListViewItem.Resources>
<ResourceDictionary>
<!-- These resources are HEAVILY copied from the command palette -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<!-- TextBox colors ! -->
<SolidColorBrush x:Key="TextControlBackground"
Color="#333333" />
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush"
Color="#B5B5B5" />
<SolidColorBrush x:Key="TextControlForeground"
Color="#B5B5B5" />
<SolidColorBrush x:Key="TextControlBorderBrush"
Color="#404040" />
<SolidColorBrush x:Key="TextControlButtonForeground"
Color="#B5B5B5" />
<SolidColorBrush x:Key="TextControlBackgroundPointerOver"
Color="#404040" />
<SolidColorBrush x:Key="TextControlForegroundPointerOver"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver"
Color="#404040" />
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver"
Color="#FF4343" />
<SolidColorBrush x:Key="TextControlBackgroundFocused"
Color="#333333" />
<SolidColorBrush x:Key="TextControlForegroundFocused"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlBorderBrushFocused"
Color="#404040" />
<SolidColorBrush x:Key="TextControlButtonForegroundPressed"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed"
Color="#FF4343" />
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<!-- TextBox colors ! -->
<SolidColorBrush x:Key="TextControlBackground"
Color="#CCCCCC" />
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush"
Color="#636363" />
<SolidColorBrush x:Key="TextControlBorderBrush"
Color="#636363" />
<SolidColorBrush x:Key="TextControlButtonForeground"
Color="#636363" />
<SolidColorBrush x:Key="TextControlBackgroundPointerOver"
Color="#DADADA" />
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver"
Color="#636363" />
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver"
Color="#FF4343" />
<SolidColorBrush x:Key="TextControlBackgroundFocused"
Color="#CCCCCC" />
<SolidColorBrush x:Key="TextControlBorderBrushFocused"
Color="#636363" />
<SolidColorBrush x:Key="TextControlButtonForegroundPressed"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed"
Color="#FF4343" />
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<!-- KeyChordText styles (use XAML defaults for High Contrast theme) -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border" />
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<local:StringIsEmptyConverter x:Key="CommandKeyChordVisibilityConverter" />
<model:IconPathConverter x:Key="IconSourceConverter" />
</ResourceDictionary>
</ListViewItem.Resources>
<!--This layout is strongly inspired by the Command Palette-->
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<!-- command label -->
<ColumnDefinition Width="*"
MaxWidth="400" />
<!-- key chord -->
<ColumnDefinition Width="*"
MaxWidth="100" />
<!-- edit button -->
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{x:Bind Action.Name, Mode=OneWay}" />
<!--
Inexplicably, we don't need to set the
AutomationProperties to Raw here, unlike in the
CommandPalette. We're not quite sure why.
-->
<Border Grid.Column="1"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Style="{ThemeResource KeyChordBorderStyle}">
<TextBlock FontSize="12"
Style="{ThemeResource KeyChordTextBlockStyle}"
Text="{x:Bind Action.KeyChordText, Mode=OneWay}" />
</Border>
<!-- Edit Button -->
<Button x:Name="EditButton"
Grid.Column="2"
Padding="3">
<!--CARLOS: Click="Edit_Click">-->
<FontIcon FontSize="12"
Glyph="&#xE70F;" />
</Button>
<!-- Edit Mode Controls -->
<StackPanel x:Name="EditModeStackPanel"
Grid.Column="2"
Orientation="Horizontal"
Visibility="Collapsed">
<!-- Cancel editing the action -->
<Button x:Uid="EditCancel">
<!--CARLOS Click="EditCancel_Click">-->
<FontIcon FontSize="12"
Glyph="&#xE711;" />
</Button>
<!-- Delete the current key binding -->
<Button x:Uid="Delete">
<!--CARLOS Click="Delete_Click">-->
<Button.Content>
<FontIcon FontSize="12"
Glyph="&#xE74D;" />
</Button.Content>
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackground"
Color="Firebrick" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="#C23232" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="#A21212" />
<SolidColorBrush x:Key="ButtonForeground"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="White" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackground"
Color="Firebrick" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="#C23232" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="#A21212" />
<SolidColorBrush x:Key="ButtonForeground"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="White" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="ButtonBackground"
Color="{ThemeResource SystemColorButtonFaceColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="ButtonForeground"
Color="{ThemeResource SystemColorButtonTextColor}" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="{ThemeResource SystemColorHighlightTextColor}" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="{ThemeResource SystemColorHighlightTextColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
</Button>
</StackPanel>
</Grid>
</ListViewItem>

View File

@@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "NullCheckToVisibilityConverter.g.h"
#include "../inc/cppwinrt_utils.h"
DECLARE_CONVERTER(winrt::Microsoft::Terminal::Settings::Editor, NullCheckToVisibilityConverter);

View File

@@ -264,14 +264,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (clickedItemTag == actionsTag)
{
auto actionsState{ winrt::make<ActionsPageNavigationState>(_settingsClone) };
actionsState.OpenJson([weakThis = get_weak()](auto&&, auto&& arg) {
if (auto self{ weakThis.get() })
{
self->_OpenJsonHandlers(nullptr, arg);
}
});
contentFrame().Navigate(xaml_typename<Editor::Actions>(), actionsState);
contentFrame().Navigate(xaml_typename<Editor::Actions>(), winrt::make<ActionsPageNavigationState>(_settingsClone));
}
else if (clickedItemTag == colorSchemesTag)
{

View File

@@ -81,6 +81,9 @@
<ClInclude Include="Interaction.h">
<DependentUpon>Interaction.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="KeyBindingContainer.h">
<DependentUpon>KeyBindingContainer.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Launch.h">
<DependentUpon>Launch.xaml</DependentUpon>
</ClInclude>
@@ -117,6 +120,9 @@
<Page Include="Interaction.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="KeyBindingContainer.xaml">
<Type>Designer</Type>
</Page>
<Page Include="Launch.xaml">
<SubType>Designer</SubType>
</Page>
@@ -178,6 +184,9 @@
<ClCompile Include="Interaction.cpp">
<DependentUpon>Interaction.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="KeyBindingContainer.cpp">
<DependentUpon>KeyBindingContainer.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Launch.cpp">
<DependentUpon>Launch.xaml</DependentUpon>
</ClCompile>
@@ -216,6 +225,10 @@
<DependentUpon>ColorSchemes.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="KeyBindingContainer.idl">
<DependentUpon>KeyBindingContainer.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="Launch.idl">
<DependentUpon>Launch.xaml</DependentUpon>
<SubType>Code</SubType>

View File

@@ -20,27 +20,6 @@ Author(s):
#include "SettingContainer.g.h"
#include "Utils.h"
// This macro defines a dependency property for a WinRT class.
// Use this in your class' header file after declaring it in the idl.
// Remember to register your dependency property in the respective cpp file.
#define DEPENDENCY_PROPERTY(type, name) \
public: \
static winrt::Windows::UI::Xaml::DependencyProperty name##Property() \
{ \
return _##name##Property; \
} \
type name() const \
{ \
return winrt::unbox_value<type>(GetValue(_##name##Property)); \
} \
void name(type const& value) \
{ \
SetValue(_##name##Property, winrt::box_value(value)); \
} \
\
private: \
static winrt::Windows::UI::Xaml::DependencyProperty _##name##Property;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct SettingContainer : SettingContainerT<SettingContainer>

View File

@@ -70,6 +70,27 @@ private:
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Editor::EnumEntry> _##name##List; \
winrt::Windows::Foundation::Collections::IMap<enumType, winrt::Microsoft::Terminal::Settings::Editor::EnumEntry> _##name##Map;
// This macro defines a dependency property for a WinRT class.
// Use this in your class' header file after declaring it in the idl.
// Remember to register your dependency property in the respective cpp file.
#define DEPENDENCY_PROPERTY(type, name) \
public: \
static winrt::Windows::UI::Xaml::DependencyProperty name##Property() \
{ \
return _##name##Property; \
} \
type name() const \
{ \
return winrt::unbox_value<type>(GetValue(_##name##Property)); \
} \
void name(type const& value) \
{ \
SetValue(_##name##Property, winrt::box_value(value)); \
} \
\
private: \
static winrt::Windows::UI::Xaml::DependencyProperty _##name##Property;
namespace winrt::Microsoft::Terminal::Settings
{
winrt::hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable);

View File

@@ -200,7 +200,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// appended to this vector.
// Return Value:
// - a deserialized ActionAndArgs corresponding to the values in json, or
// null if we failed to deserialize an action.
// an "invalid" action if we failed to deserialize an action.
winrt::com_ptr<ActionAndArgs> ActionAndArgs::FromJson(const Json::Value& json,
std::vector<SettingsLoadWarnings>& warnings)
{
@@ -255,22 +255,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// if an arg parser was registered, but failed, bail
if (pfn && args == nullptr)
{
return nullptr;
return make_self<ActionAndArgs>();
}
}
if (action != ShortcutAction::Invalid)
{
auto actionAndArgs = winrt::make_self<ActionAndArgs>();
actionAndArgs->Action(action);
actionAndArgs->Args(args);
return actionAndArgs;
}
else
{
return nullptr;
}
// Something like
// { name: "foo", action: "unbound" }
// will _remove_ the "foo" command, by returning an "invalid" action here.
return make_self<ActionAndArgs>(action, args);
}
com_ptr<ActionAndArgs> ActionAndArgs::Copy() const

View File

@@ -4,6 +4,7 @@
#pragma once
#include "ActionAndArgs.g.h"
#include "ActionArgs.h"
#include "TerminalWarnings.h"
#include "../inc/cppwinrt_utils.h"

View File

@@ -34,6 +34,7 @@
#include "../../cascadia/inc/cppwinrt_utils.h"
#include "JsonUtils.h"
#include "HashUtils.h"
#include "TerminalWarnings.h"
#include "TerminalSettingsSerializationHelpers.h"
@@ -44,6 +45,18 @@
// * ActionEventArgs holds a single IActionArgs. For events that don't need
// additional args, this can be nullptr.
template<>
constexpr size_t Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(const winrt::Microsoft::Terminal::Settings::Model::IActionArgs& args)
{
return gsl::narrow_cast<size_t>(args.Hash());
}
template<>
constexpr size_t Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(const winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs& args)
{
return gsl::narrow_cast<size_t>(args.Hash());
}
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
using namespace ::Microsoft::Terminal::Settings::Model;
@@ -124,6 +137,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_ColorScheme = _ColorScheme;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Commandline, _StartingDirectory, _TabTitle, _TabColor, _ProfileIndex, _Profile, _SuppressApplicationTitle, _ColorScheme);
}
};
struct CopyTextArgs : public CopyTextArgsT<CopyTextArgs>
@@ -164,6 +181,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_CopyFormatting = _CopyFormatting;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SingleLine, _CopyFormatting);
}
};
struct NewTabArgs : public NewTabArgsT<NewTabArgs>
@@ -198,6 +219,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_TerminalArgs = _TerminalArgs.Copy();
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_TerminalArgs);
}
};
struct SwitchToTabArgs : public SwitchToTabArgsT<SwitchToTabArgs>
@@ -234,6 +259,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_TabIndex = _TabIndex;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_TabIndex);
}
};
struct ResizePaneArgs : public ResizePaneArgsT<ResizePaneArgs>
@@ -275,6 +304,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_ResizeDirection = _ResizeDirection;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_ResizeDirection);
}
};
struct MoveFocusArgs : public MoveFocusArgsT<MoveFocusArgs>
@@ -319,6 +352,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_FocusDirection = _FocusDirection;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_FocusDirection);
}
};
struct AdjustFontSizeArgs : public AdjustFontSizeArgsT<AdjustFontSizeArgs>
@@ -353,6 +390,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Delta = _Delta;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Delta);
}
};
struct SendInputArgs : public SendInputArgsT<SendInputArgs>
@@ -390,6 +431,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Input = _Input;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Input);
}
};
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
@@ -457,6 +502,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_SplitSize = _SplitSize;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SplitStyle, _TerminalArgs, _SplitMode, _SplitSize);
}
};
struct OpenSettingsArgs : public OpenSettingsArgsT<OpenSettingsArgs>
@@ -493,6 +542,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Target = _Target;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Target);
}
};
struct SetColorSchemeArgs : public SetColorSchemeArgsT<SetColorSchemeArgs>
@@ -533,6 +586,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_SchemeName = _SchemeName;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SchemeName);
}
};
struct SetTabColorArgs : public SetTabColorArgsT<SetTabColorArgs>
@@ -567,6 +624,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_TabColor = _TabColor;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_TabColor);
}
};
struct RenameTabArgs : public RenameTabArgsT<RenameTabArgs>
@@ -601,6 +662,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Title = _Title;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Title);
}
};
struct ExecuteCommandlineArgs : public ExecuteCommandlineArgsT<ExecuteCommandlineArgs>
@@ -641,6 +706,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Commandline = _Commandline;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Commandline);
}
};
struct CloseOtherTabsArgs : public CloseOtherTabsArgsT<CloseOtherTabsArgs>
@@ -677,6 +746,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Index = _Index;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Index);
}
};
struct CloseTabsAfterArgs : public CloseTabsAfterArgsT<CloseTabsAfterArgs>
@@ -713,6 +786,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Index = _Index;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Index);
}
};
struct MoveTabArgs : public MoveTabArgsT<MoveTabArgs>
@@ -756,6 +833,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Direction = _Direction;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Direction);
}
};
struct ScrollUpArgs : public ScrollUpArgsT<ScrollUpArgs>
@@ -790,6 +871,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_RowsToScroll = _RowsToScroll;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_RowsToScroll);
}
};
struct ScrollDownArgs : public ScrollDownArgsT<ScrollDownArgs>
@@ -824,6 +909,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_RowsToScroll = _RowsToScroll;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_RowsToScroll);
}
};
struct ToggleCommandPaletteArgs : public ToggleCommandPaletteArgsT<ToggleCommandPaletteArgs>
@@ -860,6 +949,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_LaunchMode = _LaunchMode;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_LaunchMode);
}
};
struct FindMatchArgs : public FindMatchArgsT<FindMatchArgs>
@@ -903,6 +996,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Direction = _Direction;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Direction);
}
};
struct NewWindowArgs : public NewWindowArgsT<NewWindowArgs>
@@ -937,6 +1034,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_TerminalArgs = _TerminalArgs.Copy();
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_TerminalArgs);
}
};
struct PrevTabArgs : public PrevTabArgsT<PrevTabArgs>
@@ -970,6 +1071,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_SwitcherMode = _SwitcherMode;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SwitcherMode);
}
};
struct NextTabArgs : public NextTabArgsT<NextTabArgs>
@@ -1003,6 +1108,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_SwitcherMode = _SwitcherMode;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SwitcherMode);
}
};
struct RenameWindowArgs : public RenameWindowArgsT<RenameWindowArgs>
@@ -1036,6 +1145,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Name = _Name;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Name);
}
};
}

View File

@@ -8,6 +8,7 @@ namespace Microsoft.Terminal.Settings.Model
Boolean Equals(IActionArgs other);
String GenerateName();
IActionArgs Copy();
UInt64 Hash();
};
interface IActionEventArgs
@@ -105,6 +106,7 @@ namespace Microsoft.Terminal.Settings.Model
Boolean Equals(NewTerminalArgs other);
String GenerateName();
String ToCommandline();
UInt64 Hash();
};
[default_interface] runtimeclass ActionEventArgs : IActionEventArgs

View File

@@ -0,0 +1,370 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ActionMap.h"
#include "ActionMap.g.cpp"
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Control;
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
ActionMap::ActionMap() :
_NestedCommands{ single_threaded_map<hstring, Model::Command>() }
{
}
// Method Description:
// - Retrieves the Command in the ActionList, if it's valid
// - We internally store invalid commands as full commands.
// This helper function returns nullptr when we get an invalid
// command. This allows us to simply check for null when we
// want a valid command.
// Arguments:
// - actionID: the internal ID associated with a Command
// Return Value:
// - If the command is valid, the command itself.
// - If the command is explicitly unbound, nullptr.
// - If the command cannot be found in this layer, nullopt.
std::optional<Model::Command> ActionMap::_GetActionByID(InternalActionID actionID) const
{
const auto& cmdPair{ _ActionMap.find(actionID) };
if (cmdPair == _ActionMap.end())
{
// we don't have an answer
return std::nullopt;
}
else
{
const auto& cmd{ cmdPair->second };
// ActionMap should never point to nullptr
FAIL_FAST_IF_NULL(cmd);
return !cmd.HasNestedCommands() && cmd.ActionAndArgs().Action() == ShortcutAction::Invalid ?
nullptr : // explicitly unbound
cmd;
}
}
// Method Description:
// - Retrieves a map of command names to the commands themselves
// - These commands should not be modified directly because they may result in
// an invalid state for the `ActionMap`
Windows::Foundation::Collections::IMapView<hstring, Model::Command> ActionMap::NameMap()
{
if (!_NameMapCache)
{
// populate _NameMapCache
std::unordered_map<hstring, Model::Command> nameMap{};
std::set<InternalActionID> visitedActionIDs{ ActionHash()(make<implementation::ActionAndArgs>()) };
_PopulateNameMapWithNestedCommands(nameMap);
_PopulateNameMapWithStandardCommands(nameMap, visitedActionIDs);
_NameMapCache = single_threaded_map<hstring, Model::Command>(std::move(nameMap));
}
return _NameMapCache.GetView();
}
// Method Description:
// - Populates the provided nameMap with all of our nested commands and our parents nested commands
// - Performs a top-down approach by going to the root first, then recursively adding the nested commands layer-by-layer
// Arguments:
// - nameMap: the nameMap we're populating. This maps the name (hstring) of a command to the command itself.
void ActionMap::_PopulateNameMapWithNestedCommands(std::unordered_map<hstring, Model::Command>& nameMap) const
{
// Update NameMap with our parents.
// Starting with this means we're doing a top-down approach.
for (const auto& parent : _parents)
{
parent->_PopulateNameMapWithNestedCommands(nameMap);
}
// Add NestedCommands to NameMap _after_ we handle our parents.
// This allows us to override whatever our parents tell us.
for (const auto& [name, cmd] : _NestedCommands)
{
if (cmd.HasNestedCommands())
{
// add a valid cmd
nameMap.insert_or_assign(name, cmd);
}
else
{
// remove the invalid cmd
nameMap.erase(name);
}
}
}
// Method Description:
// - Populates the provided nameMap with all of our actions and our parents actions
// while omitting the actions that were already added before
// - This needs to be a bottom up approach to ensure that we only add each action (identified by action ID) once.
// Arguments:
// - nameMap: the nameMap we're populating. This maps the name (hstring) of a command to the command itself.
// There should only ever by one of each command (identified by the actionID) in the nameMap.
// - visitedActionIDs: the actionIDs that we've already added to the nameMap. Commands with a matching actionID
// have already been added, and should be ignored.
void ActionMap::_PopulateNameMapWithStandardCommands(std::unordered_map<hstring, Model::Command>& nameMap, std::set<InternalActionID>& visitedActionIDs) const
{
// Update NameMap and visitedActionIDs with our current layer
for (const auto& [actionID, cmd] : _ActionMap)
{
// Only populate NameMap with actions that haven't been visited already.
if (visitedActionIDs.find(actionID) == visitedActionIDs.end())
{
const auto& name{ cmd.Name() };
if (!name.empty())
{
// Update NameMap.
nameMap.insert_or_assign(name, cmd);
}
// Record that we already handled adding this action to the NameMap.
visitedActionIDs.insert(actionID);
}
}
// Update NameMap and visitedActionIDs with our parents
for (const auto& parent : _parents)
{
parent->_PopulateNameMapWithStandardCommands(nameMap, visitedActionIDs);
}
}
com_ptr<ActionMap> ActionMap::Copy() const
{
auto actionMap{ make_self<ActionMap>() };
// copy _KeyMap (KeyChord --> ID)
std::for_each(_KeyMap.begin(), _KeyMap.end(), [actionMap](const auto& pair) {
actionMap->_KeyMap.insert(pair);
});
// copy _ActionMap (ID --> Command)
for (const auto& [actionID, cmd] : _ActionMap)
{
actionMap->_ActionMap.insert({ actionID, *(get_self<Command>(cmd)->Copy()) });
}
// copy _NestedCommands (Name --> Command)
for (const auto& [name, cmd] : _NestedCommands)
{
actionMap->_NestedCommands.Insert(name, *(get_self<Command>(cmd)->Copy()));
}
// repeat this for each of our parents
for (const auto& parent : _parents)
{
actionMap->_parents.push_back(std::move(parent->Copy()));
}
return actionMap;
}
// Method Description:
// - Adds a command to the ActionMap
// Arguments:
// - cmd: the command we're adding
void ActionMap::AddAction(const Model::Command& cmd)
{
// _Never_ add null to the ActionMap
if (!cmd)
{
return;
}
// Handle nested commands
const auto cmdImpl{ get_self<Command>(cmd) };
if (cmdImpl->IsNestedCommand())
{
// But check if it actually has a name to bind to first
const auto name{ cmd.Name() };
if (!name.empty())
{
_NestedCommands.Insert(name, cmd);
}
return;
}
// General Case:
// Add the new command to the NameMap and KeyMap (whichever is applicable).
// These maps direct you to an entry in the ActionMap.
// Removing Actions from the Command Palette:
// cmd.Name and cmd.Action have a one-to-one relationship.
// If cmd.Name is empty, we must retrieve the old name and remove it.
// Removing Key Bindings:
// cmd.Keys and cmd.Action have a many-to-one relationship.
// If cmd.Keys is empty, we don't care.
// If action is "unbound"/"invalid", you're explicitly unbinding the provided cmd.keys.
Model::Command oldCmd{ nullptr };
const auto actionID{ ActionHash()(cmd.ActionAndArgs()) };
auto actionPair{ _ActionMap.find(actionID) };
if (actionPair != _ActionMap.end())
{
// We're adding an action that already exists.
// Record it and update it with any new information.
oldCmd = actionPair->second;
// Update Name
const auto cmdImpl{ get_self<Command>(cmd) };
if (cmdImpl->HasName())
{
// This command has a name, check if it's new.
const auto newName{ cmd.Name() };
if (newName != oldCmd.Name())
{
// The new name differs from the old name,
// update our name.
auto oldCmdImpl{ get_self<Command>(oldCmd) };
oldCmdImpl->Name(newName);
}
// Handle a collision with NestedCommands
_NestedCommands.TryRemove(newName);
}
}
else
{
// add this action in for the first time
_ActionMap.insert({ actionID, cmd });
// Handle a collision with NestedCommands
_NestedCommands.TryRemove(cmd.Name());
}
// Update KeyMap
if (const auto keys{ cmd.Keys() })
{
const auto oldKeyPair{ _KeyMap.find(keys) };
if (oldKeyPair != _KeyMap.end())
{
// Collision: The key chord was already in use.
// Remove the old one.
actionPair = _ActionMap.find(oldKeyPair->second);
const auto& conflictingCmd{ actionPair->second };
const auto& conflictingCmdImpl{ get_self<implementation::Command>(conflictingCmd) };
conflictingCmdImpl->EraseKey(keys);
}
// Assign the new action.
_KeyMap.insert_or_assign(keys, actionID);
if (oldCmd)
{
// Update inner Command with new key chord
auto oldCmdImpl{ get_self<Command>(oldCmd) };
oldCmdImpl->RegisterKey(keys);
}
}
}
// Method Description:
// - Retrieves the assigned command that can be invoked with the given key chord
// Arguments:
// - keys: the key chord of the command to search for
// Return Value:
// - the command with the given key chord
// - nullptr if the key chord is explicitly unbound
Model::Command ActionMap::GetActionByKeyChord(Control::KeyChord const& keys) const
{
// Check the current layer
const auto& cmd{ _GetActionByKeyChordInternal(keys) };
if (cmd.has_value())
{
return *cmd;
}
// This key chord is not explicitly bound
return nullptr;
}
// Method Description:
// - Retrieves the assigned command with the given key chord.
// - Can return nullopt to differentiate explicit unbinding vs lack of binding.
// Arguments:
// - keys: the key chord of the command to search for
// Return Value:
// - the command with the given key chord
// - nullptr if the key chord is explicitly unbound
// - nullopt if it was not bound in this layer
std::optional<Model::Command> ActionMap::_GetActionByKeyChordInternal(Control::KeyChord const& keys) const
{
// Check the current layer
const auto actionIDPair{ _KeyMap.find(keys) };
if (actionIDPair != _KeyMap.end())
{
// the command was explicitly bound,
// return what we found (invalid commands exposed as nullptr)
return _GetActionByID(actionIDPair->second);
}
// the command was not bound in this layer,
// ask my parents
for (const auto& parent : _parents)
{
const auto& inheritedCmd{ parent->_GetActionByKeyChordInternal(keys) };
if (inheritedCmd.has_value())
{
return *inheritedCmd;
}
}
// This action is not explicitly bound
return std::nullopt;
}
// Method Description:
// - Retrieves the key chord for the provided action
// Arguments:
// - action: the shortcut action (an action type) we're looking for
// Return Value:
// - the key chord that executes the given action
// - nullptr if the action is not bound to a key chord
Control::KeyChord ActionMap::GetKeyBindingForAction(ShortcutAction const& action) const
{
return GetKeyBindingForAction(action, nullptr);
}
// Method Description:
// - Retrieves the key chord for the provided action
// Arguments:
// - action: the shortcut action (an action type) we're looking for
// - myArgs: the action args for the action we're looking for
// Return Value:
// - the key chord that executes the given action
// - nullptr if the action is not bound to a key chord
Control::KeyChord ActionMap::GetKeyBindingForAction(ShortcutAction const& myAction, IActionArgs const& myArgs) const
{
if (myAction == ShortcutAction::Invalid)
{
return nullptr;
}
// Check our internal state.
const ActionAndArgs& actionAndArgs{ myAction, myArgs };
const auto hash{ ActionHash()(actionAndArgs) };
if (const auto& cmd{ _GetActionByID(hash) })
{
return cmd.value().Keys();
}
// Check our parents
for (const auto& parent : _parents)
{
if (const auto& keys{ parent->GetKeyBindingForAction(myAction, myArgs) })
{
return keys;
}
}
// This key binding does not exist
return nullptr;
}
}

View File

@@ -0,0 +1,107 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- ActionMap.h
Abstract:
- A mapping of key chords to actions. Includes (de)serialization logic.
Author(s):
- Carlos Zamora - September 2020
--*/
#pragma once
#include "ActionMap.g.h"
#include "IInheritable.h"
#include "Command.h"
#include "../inc/cppwinrt_utils.h"
// fwdecl unittest classes
namespace SettingsModelLocalTests
{
class KeyBindingsTests;
class DeserializationTests;
class TerminalSettingsTests;
}
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
typedef size_t InternalActionID;
struct KeyChordHash
{
std::size_t operator()(const Control::KeyChord& key) const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(key.Modifiers(), key.Vkey());
}
};
struct KeyChordEquality
{
bool operator()(const Control::KeyChord& lhs, const Control::KeyChord& rhs) const
{
return lhs.Modifiers() == rhs.Modifiers() && lhs.Vkey() == rhs.Vkey();
}
};
struct ActionHash
{
InternalActionID operator()(const Model::ActionAndArgs& actionAndArgs) const
{
std::hash<Model::ShortcutAction> actionHash;
std::size_t hashedAction{ actionHash(actionAndArgs.Action()) };
std::size_t hashedArgs;
if (const auto& args{ actionAndArgs.Args() })
{
hashedArgs = gsl::narrow_cast<size_t>(args.Hash());
}
else
{
std::hash<IActionArgs> argsHash;
hashedArgs = argsHash(nullptr);
}
return hashedAction ^ hashedArgs;
}
};
struct ActionMap : ActionMapT<ActionMap>, IInheritable<ActionMap>
{
ActionMap();
// views
Windows::Foundation::Collections::IMapView<hstring, Model::Command> NameMap();
com_ptr<ActionMap> Copy() const;
// queries
Model::Command GetActionByKeyChord(Control::KeyChord const& keys) const;
Control::KeyChord GetKeyBindingForAction(ShortcutAction const& action) const;
Control::KeyChord GetKeyBindingForAction(ShortcutAction const& action, IActionArgs const& actionArgs) const;
// population
void AddAction(const Model::Command& cmd);
std::vector<SettingsLoadWarnings> LayerJson(const Json::Value& json);
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(Control::KeyModifiers modifiers);
private:
std::optional<Model::Command> _GetActionByID(InternalActionID actionID) const;
std::optional<Model::Command> _GetActionByKeyChordInternal(Control::KeyChord const& keys) const;
void _PopulateNameMapWithNestedCommands(std::unordered_map<hstring, Model::Command>& nameMap) const;
void _PopulateNameMapWithStandardCommands(std::unordered_map<hstring, Model::Command>& nameMap, std::set<InternalActionID>& visitedActionIDs) const;
Windows::Foundation::Collections::IMap<hstring, Model::Command> _NameMapCache{ nullptr };
Windows::Foundation::Collections::IMap<hstring, Model::Command> _NestedCommands{ nullptr };
std::unordered_map<Control::KeyChord, InternalActionID, KeyChordHash, KeyChordEquality> _KeyMap;
std::unordered_map<InternalActionID, Model::Command> _ActionMap;
friend class SettingsModelLocalTests::KeyBindingsTests;
friend class SettingsModelLocalTests::DeserializationTests;
friend class SettingsModelLocalTests::TerminalSettingsTests;
};
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "Command.idl";
namespace Microsoft.Terminal.Settings.Model
{
[default_interface] runtimeclass ActionMap {
Command GetActionByKeyChord(Microsoft.Terminal.Control.KeyChord keys);
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action);
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action, IActionArgs actionArgs);
Windows.Foundation.Collections.IMapView<String, Command> NameMap { get; };
}
}

View File

@@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// - A couple helper functions for serializing/deserializing a KeyMapping
// to/from json.
//
// Author(s):
// - Mike Griese - May 2019
#include "pch.h"
#include "ActionMap.h"
#include "ActionAndArgs.h"
#include "KeyChordSerialization.h"
#include "JsonUtils.h"
#include "Command.h"
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
// Method Description:
// - Deserialize an ActionMap from the array `json`. The json array should contain
// an array of serialized `Command` objects.
// - These actions are added to the `ActionMap`, where we automatically handle
// overwriting and unbinding actions.
// Arguments:
// - json: an array of Json::Value's to deserialize into our ActionMap.
// Return value:
// - a list of warnings encountered while deserializing the json
std::vector<SettingsLoadWarnings> ActionMap::LayerJson(const Json::Value& json)
{
// It's possible that the user provided keybindings have some warnings in
// them - problems that we should alert the user to, but we can recover
// from. Most of these warnings cannot be detected later in the Validate
// settings phase, so we'll collect them now.
std::vector<SettingsLoadWarnings> warnings;
for (const auto& cmdJson : json)
{
if (!cmdJson.isObject())
{
continue;
}
AddAction(*Command::FromJson(cmdJson, warnings));
}
return warnings;
}
// Method Description:
// - Takes the KeyModifier flags from Terminal and maps them to the WinRT types which are used by XAML
// Return Value:
// - a Windows::System::VirtualKeyModifiers object with the flags of which modifiers used.
Windows::System::VirtualKeyModifiers ActionMap::ConvertVKModifiers(KeyModifiers modifiers)
{
Windows::System::VirtualKeyModifiers keyModifiers = Windows::System::VirtualKeyModifiers::None;
if (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Control;
}
if (WI_IsFlagSet(modifiers, KeyModifiers::Shift))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Shift;
}
if (WI_IsFlagSet(modifiers, KeyModifiers::Alt))
{
// note: Menu is the Alt VK_MENU
keyModifiers |= Windows::System::VirtualKeyModifiers::Menu;
}
if (WI_IsFlagSet(modifiers, KeyModifiers::Windows))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Windows;
}
return keyModifiers;
}
}

View File

@@ -186,9 +186,9 @@ IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Profile> Cascadia
// - <none>
// Return Value:
// - the globally configured keybindings
winrt::Microsoft::Terminal::Settings::Model::KeyMapping CascadiaSettings::KeyMap() const noexcept
winrt::Microsoft::Terminal::Settings::Model::ActionMap CascadiaSettings::ActionMap() const noexcept
{
return _globals->KeyMap();
return _globals->ActionMap();
}
// Method Description:
@@ -780,7 +780,7 @@ void CascadiaSettings::_ValidateKeybindings()
void CascadiaSettings::_ValidateColorSchemesInCommands()
{
bool foundInvalidScheme{ false };
for (const auto& nameAndCmd : _globals->Commands())
for (const auto& nameAndCmd : _globals->ActionMap().NameMap())
{
if (_HasInvalidColorScheme(nameAndCmd.Value()))
{
@@ -809,7 +809,7 @@ bool CascadiaSettings::_HasInvalidColorScheme(const Model::Command& command)
}
}
}
else if (const auto& actionAndArgs = command.Action())
else if (const auto& actionAndArgs = command.ActionAndArgs())
{
if (const auto& realArgs = actionAndArgs.Args().try_as<Model::SetColorSchemeArgs>())
{

View File

@@ -70,7 +70,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Model::GlobalAppSettings GlobalSettings() const;
Windows::Foundation::Collections::IObservableVector<Model::Profile> AllProfiles() const noexcept;
Windows::Foundation::Collections::IObservableVector<Model::Profile> ActiveProfiles() const noexcept;
Model::KeyMapping KeyMap() const noexcept;
Model::ActionMap ActionMap() const noexcept;
static com_ptr<CascadiaSettings> FromJson(const Json::Value& json);
void LayerJson(const Json::Value& json);

View File

@@ -30,7 +30,7 @@ namespace Microsoft.Terminal.Settings.Model
Windows.Foundation.Collections.IObservableVector<Profile> AllProfiles { get; };
Windows.Foundation.Collections.IObservableVector<Profile> ActiveProfiles { get; };
KeyMapping KeyMap { get; };
ActionMap ActionMap { get; };
Windows.Foundation.Collections.IVectorView<SettingsLoadWarnings> Warnings { get; };
Windows.Foundation.IReference<SettingsLoadErrors> GetLoadingError { get; };

View File

@@ -248,57 +248,6 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
// If this throws, the app will catch it and use the default settings
resultPtr->_ValidateSettings();
// GH 3855 - Gathering Data on custom profiles to inform better defaults
// Do it after everything else so it won't happen unless validation passed.
// Also, avoid processing unless someone's listening for measures. The keybindings work, at least,
// is a lot of computation we can skip if no one cares.
if (TraceLoggingProviderEnabled(g_hSettingsModelProvider, 0, MICROSOFT_KEYWORD_MEASURES))
{
const auto guid = resultPtr->GlobalSettings().DefaultProfile();
// Compare to the defaults.json one that we set on install.
// If it's different, log what the user chose.
if (hardcodedDefaultGuid != guid)
{
TraceLoggingWrite(
g_hSettingsModelProvider, // handle to TerminalApp tracelogging provider
"CustomDefaultProfile",
TraceLoggingDescription("Event emitted when user has chosen a different default profile than hardcoded one on load/reload"),
TraceLoggingGuid(guid, "DefaultProfile", "ID of user-chosen default profile"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
// If the user had keybinding settings preferences, we want to learn from them to make better defaults
auto userKeybindings = resultPtr->_userSettings[JsonKey(LegacyKeybindingsKey)];
if (!userKeybindings.empty())
{
// If there are custom key bindings, let's understand what they are because maybe the defaults aren't good enough
// Run it through the object so we can parse it apart and then only serialize the fields we're interested in
// and avoid extraneous data.
auto km = winrt::make_self<implementation::KeyMapping>();
km->LayerJson(userKeybindings);
auto value = km->ToJson();
// Reserialize the keybindings
Json::StreamWriterBuilder wbuilder;
// Use 4 spaces to indent instead of \t
wbuilder.settings_["indentation"] = " ";
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
const auto keybindingsString = Json::writeString(wbuilder, value);
TraceLoggingWrite(
g_hSettingsModelProvider, // handle to TerminalApp tracelogging provider
"CustomKeybindings",
TraceLoggingDescription("Event emitted when custom keybindings are identified on load/reload"),
TraceLoggingUtf8String(keybindingsString.c_str(), "Keybindings", "Keybindings as JSON"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
}
return *resultPtr;
}
catch (const SettingsException& ex)

View File

@@ -6,7 +6,7 @@
#include "Command.g.cpp"
#include "ActionAndArgs.h"
#include "JsonUtils.h"
#include "KeyChordSerialization.h"
#include <LibraryResources.h>
#include "TerminalSettingsSerializationHelpers.h"
@@ -26,6 +26,7 @@ static constexpr std::string_view ActionKey{ "command" };
static constexpr std::string_view ArgsKey{ "args" };
static constexpr std::string_view IterateOnKey{ "iterateOn" };
static constexpr std::string_view CommandsKey{ "commands" };
static constexpr std::string_view KeysKey{ "keys" };
static constexpr std::string_view ProfileNameToken{ "${profile.name}" };
static constexpr std::string_view ProfileIconToken{ "${profile.icon}" };
@@ -35,16 +36,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
Command::Command()
{
_setAction(nullptr);
}
com_ptr<Command> Command::Copy() const
{
auto command{ winrt::make_self<Command>() };
command->_Name = _Name;
command->_Action = _Action;
command->_KeyChordText = _KeyChordText;
command->_IconPath = _IconPath;
command->_name = _name;
// TODO GH#6900: We probably want ActionAndArgs::Copy here
// This is fine for now because SUI can't actually
// modify the copy yet.
command->_ActionAndArgs = _ActionAndArgs;
for (const auto& keys : _keyMappings)
{
command->_keyMappings.emplace_back(keys.Modifiers(), keys.Vkey());
}
command->_iconPath = _iconPath;
command->_IterateOn = _IterateOn;
command->_originalJson = _originalJson;
@@ -65,10 +72,158 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return _subcommands ? _subcommands.GetView() : nullptr;
}
// Function Description:
// - reports if the current command has nested commands
// - This CANNOT detect { "name": "foo", "commands": null }
bool Command::HasNestedCommands() const
{
return _subcommands ? _subcommands.Size() > 0 : false;
}
// Function Description:
// - reports if the current command IS a nested command
// - This CAN be used to detect cases like { "name": "foo", "commands": null }
bool Command::IsNestedCommand() const noexcept
{
return _nestedCommand;
}
bool Command::HasName() const noexcept
{
return _name.has_value();
}
hstring Command::Name() const noexcept
{
if (_name.has_value())
{
// name was explicitly set, return that value.
return hstring{ _name.value() };
}
else if (_ActionAndArgs)
{
// generate a name from our action
return get_self<implementation::ActionAndArgs>(_ActionAndArgs)->GenerateName();
}
else
{
// we have no name
return {};
}
}
void Command::Name(const hstring& value)
{
if (!_name.has_value() || _name.value() != value)
{
_name = value;
}
}
std::vector<Control::KeyChord> Command::KeyMappings() const noexcept
{
return _keyMappings;
}
// Function Description:
// - Add the key chord to the command's list of key mappings.
// - If the key chord was already registered, move it to the back
// of the line, and dispatch a notification that Command::Keys changed.
// Arguments:
// - keys: the new key chord that we are registering this command to
// Return Value:
// - <none>
void Command::RegisterKey(const Control::KeyChord& keys)
{
if (!keys)
{
return;
}
// Check if we registered this key chord before
for (auto pos = _keyMappings.begin(); pos < _keyMappings.end(); ++pos)
{
if (keys.Modifiers() == pos->Modifiers() && keys.Vkey() == pos->Vkey())
{
// KeyChord was already registered.
if (*pos == _keyMappings.back())
{
// It's already the latest key registered.
return;
}
else
{
// Move the new KeyChord to the back of the line.
_keyMappings.erase(pos);
_keyMappings.push_back(*pos);
return;
}
}
}
// Add the KeyChord to the back of the line.
_keyMappings.push_back(keys);
}
// Function Description:
// - Remove the key chord from the command's list of key mappings.
// Arguments:
// - keys: the key chord that we are unregistering
// Return Value:
// - <none>
void Command::EraseKey(const Control::KeyChord& keys)
{
for (auto pos = _keyMappings.begin(); pos < _keyMappings.end(); ++pos)
{
if (keys.Modifiers() == pos->Modifiers() && keys.Vkey() == pos->Vkey())
{
// Found the KeyChord, remove it.
_keyMappings.erase(pos);
return;
}
}
}
// Function Description:
// - Keys is the Command's identifying KeyChord. The command may have multiple keys associated
// with it, but we'll only ever display the most recently added one externally. To do this,
// _keyMappings stores all of the associated key chords, but ensures that the last entry
// is the most recently added one.
// Arguments:
// - <none>
// Return Value:
// - the primary key chord associated with this Command
Control::KeyChord Command::Keys() const noexcept
{
if (_keyMappings.empty())
{
return nullptr;
}
return _keyMappings.back();
}
hstring Command::KeyChordText() const noexcept
{
return KeyChordSerialization::ToString(Keys());
}
hstring Command::IconPath() const noexcept
{
if (_iconPath.has_value())
{
return hstring{ *_iconPath };
}
return {};
}
void Command::IconPath(const hstring& val)
{
if (!_iconPath.has_value() || _iconPath.value() != val)
{
_iconPath = val;
}
}
// Function Description:
// - attempt to get the name of this command from the provided json object.
// * If the "name" property is a string, return that value.
@@ -79,7 +234,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// - json: The Json::Value representing the command object we should get the name for.
// Return Value:
// - the empty string if we couldn't find a name, otherwise the command's name.
static winrt::hstring _nameFromJson(const Json::Value& json)
static std::optional<std::wstring> _nameFromJson(const Json::Value& json)
{
if (const auto name{ json[JsonKey(NameKey)] })
{
@@ -89,43 +244,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
if (HasLibraryResourceWithName(*resourceKey))
{
return GetLibraryResourceString(*resourceKey);
return std::wstring{ GetLibraryResourceString(*resourceKey) };
}
}
}
else if (name.isString())
{
return JsonUtils::GetValue<winrt::hstring>(name);
return JsonUtils::GetValue<std::wstring>(name);
}
}
return L"";
}
// Method Description:
// - Get the name for the command specified in `json`. If there is no "name"
// property in the provided json object, then instead generate a name for
// the provided ActionAndArgs.
// Arguments:
// - json: json for the command to generate a name for.
// - actionAndArgs: An ActionAndArgs object to use to generate a name for,
// if the json object doesn't contain a "name".
// Return Value:
// - The "name" from the json, or the generated name from ActionAndArgs::GenerateName
static winrt::hstring _nameFromJsonOrAction(const Json::Value& json,
winrt::com_ptr<ActionAndArgs> actionAndArgs)
{
auto manualName = _nameFromJson(json);
if (!manualName.empty())
else if (json.isMember(JsonKey(NameKey)))
{
return manualName;
}
if (!actionAndArgs)
{
return L"";
// { "name": null, "command": "copy" } will land in this case, which
// should also be used for unbinding.
return std::wstring{};
}
return actionAndArgs->GenerateName();
return std::nullopt;
}
// Method Description:
@@ -158,6 +293,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
// Initialize our list of subcommands.
result->_subcommands = winrt::single_threaded_map<winrt::hstring, Model::Command>();
result->_nestedCommand = true;
auto nestedWarnings = Command::LayerJson(result->_subcommands, nestedCommandsJson);
// It's possible that the nested commands have some warnings
warnings.insert(warnings.end(), nestedWarnings.begin(), nestedWarnings.end());
@@ -165,7 +301,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
if (result->_subcommands.Size() == 0)
{
warnings.push_back(SettingsLoadWarnings::FailedToParseSubCommands);
return nullptr;
result->_ActionAndArgs = make<implementation::ActionAndArgs>();
}
nested = true;
@@ -174,59 +310,60 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
// { "name": "foo", "commands": null } will land in this case, which
// should also be used for unbinding.
return nullptr;
// create an "invalid" ActionAndArgs
result->_ActionAndArgs = make<implementation::ActionAndArgs>();
result->_nestedCommand = true;
}
JsonUtils::GetValueForKey(json, IconKey, result->_IconPath);
JsonUtils::GetValueForKey(json, IconKey, result->_iconPath);
// If we're a nested command, we can ignore the current action.
if (!nested)
{
if (const auto actionJson{ json[JsonKey(ActionKey)] })
{
auto actionAndArgs = ActionAndArgs::FromJson(actionJson, warnings);
if (actionAndArgs)
{
result->_setAction(*actionAndArgs);
}
else
{
// Something like
// { name: "foo", action: "unbound" }
// will _remove_ the "foo" command, by returning null here.
return nullptr;
}
// If an iterable command doesn't have a name set, we'll still just
// try and generate a fake name for the command give the string we
// currently have. It'll probably generate something like "New tab,
// profile: ${profile.name}". This string will only be temporarily
// used internally, so there's no problem.
result->_setName(_nameFromJsonOrAction(json, actionAndArgs));
result->_ActionAndArgs = *ActionAndArgs::FromJson(actionJson, warnings);
}
else
{
// { name: "foo", action: null } will land in this case, which
// should also be used for unbinding.
return nullptr;
// create an "invalid" ActionAndArgs
result->_ActionAndArgs = make<implementation::ActionAndArgs>();
}
// GH#4239 - If the user provided more than one key
// chord to a "keys" array, warn the user here.
// TODO: GH#1334 - remove this check.
const auto keysJson{ json[JsonKey(KeysKey)] };
if (keysJson.isArray() && keysJson.size() > 1)
{
warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord);
}
else
{
Control::KeyChord keys{ nullptr };
if (JsonUtils::GetValueForKey(json, KeysKey, keys))
{
result->RegisterKey(keys);
}
}
}
else
{
result->_setName(_nameFromJson(json));
}
// If an iterable command doesn't have a name set, we'll still just
// try and generate a fake name for the command give the string we
// currently have. It'll probably generate something like "New tab,
// profile: ${profile.name}". This string will only be temporarily
// used internally, so there's no problem.
result->_name = _nameFromJson(json);
// Stash the original json value in this object. If the command is
// iterable, we'll need to re-parse it later, once we know what all the
// values we can iterate on are.
result->_originalJson = json;
if (result->_Name.empty())
{
return nullptr;
}
return result;
}
@@ -252,23 +389,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
try
{
auto result = Command::FromJson(value, warnings);
if (result)
{
// Override commands with the same name
commands.Insert(result->Name(), *result);
}
else
const auto result = Command::FromJson(value, warnings);
if (result->ActionAndArgs().Action() == ShortcutAction::Invalid && !result->HasNestedCommands())
{
// If there wasn't a parsed command, then try to get the
// name from the json blob. If that name currently
// exists in our list of commands, we should remove it.
const auto name = _nameFromJson(value);
if (!name.empty())
if (name.has_value() && !name->empty())
{
commands.Remove(name);
commands.Remove(*name);
}
}
else
{
// Override commands with the same name
commands.Insert(result->Name(), *result);
}
}
CATCH_LOG();
}

View File

@@ -21,6 +21,7 @@ Author(s):
#include "Command.g.h"
#include "TerminalWarnings.h"
#include "Profile.h"
#include "ActionAndArgs.h"
#include "../inc/cppwinrt_utils.h"
#include "SettingsTypes.h"
@@ -49,21 +50,35 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static std::vector<SettingsLoadWarnings> LayerJson(Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command>& commands,
const Json::Value& json);
bool HasNestedCommands() const;
bool IsNestedCommand() const noexcept;
Windows::Foundation::Collections::IMapView<winrt::hstring, Model::Command> NestedCommands() const;
bool HasName() const noexcept;
hstring Name() const noexcept;
void Name(const hstring& name);
Control::KeyChord Keys() const noexcept;
hstring KeyChordText() const noexcept;
std::vector<Control::KeyChord> KeyMappings() const noexcept;
void RegisterKey(const Control::KeyChord& keys);
void EraseKey(const Control::KeyChord& keys);
hstring IconPath() const noexcept;
void IconPath(const hstring& val);
winrt::Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker propertyChangedRevoker;
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(Model::ActionAndArgs, Action, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, KeyChordText, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, IconPath, _PropertyChangedHandlers);
WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None);
WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs);
private:
Json::Value _originalJson;
Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command> _subcommands{ nullptr };
std::vector<Control::KeyChord> _keyMappings;
std::optional<std::wstring> _name;
std::optional<std::wstring> _iconPath;
bool _nestedCommand{ false };
static std::vector<Model::Command> _expandCommand(Command* const expandable,
Windows::Foundation::Collections::IVectorView<Model::Profile> profiles,

View File

@@ -1,20 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "KeyMapping.idl";
#include "AllShortcutActions.h"
import "ActionArgs.idl";
import "Profile.idl";
import "ColorScheme.idl";
import "TerminalWarnings.idl";
namespace Microsoft.Terminal.Settings.Model
{
enum ShortcutAction
{
Invalid = 0, // treat Invalid as unbound actions
// When adding a new action, add them to AllShortcutActions.h!
#define ON_ALL_ACTIONS(action) action,
ALL_SHORTCUT_ACTIONS
#undef ON_ALL_ACTIONS
};
[default_interface] runtimeclass ActionAndArgs {
ActionAndArgs();
ActionAndArgs(ShortcutAction action, IActionArgs args);
IActionArgs Args;
ShortcutAction Action;
};
[default_interface] runtimeclass Command : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
Command();
String Name;
ActionAndArgs Action;
String KeyChordText;
String Name { get; };
ActionAndArgs ActionAndArgs { get; };
Microsoft.Terminal.Control.KeyChord Keys { get; };
void RegisterKey(Microsoft.Terminal.Control.KeyChord keys);
String KeyChordText { get; };
String IconPath;

View File

@@ -64,12 +64,11 @@ bool GlobalAppSettings::_getDefaultDebugFeaturesValue()
}
GlobalAppSettings::GlobalAppSettings() :
_keymap{ winrt::make_self<KeyMapping>() },
_actionMap{ winrt::make_self<implementation::ActionMap>() },
_keybindingsWarnings{},
_validDefaultProfile{ false },
_defaultProfile{}
{
_commands = winrt::single_threaded_map<winrt::hstring, Model::Command>();
_colorSchemes = winrt::single_threaded_map<winrt::hstring, Model::ColorScheme>();
}
@@ -85,10 +84,9 @@ void GlobalAppSettings::_FinalizeInheritance()
FAIL_FAST_IF(_parents.size() > 1);
for (auto parent : _parents)
{
_keymap = std::move(parent->_keymap);
_actionMap->InsertParent(parent->_actionMap);
_keybindingsWarnings = std::move(parent->_keybindingsWarnings);
_colorSchemes = std::move(parent->_colorSchemes);
_commands = std::move(parent->_commands);
}
}
@@ -128,10 +126,7 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile;
globals->_validDefaultProfile = _validDefaultProfile;
globals->_defaultProfile = _defaultProfile;
if (_keymap)
{
globals->_keymap = _keymap->Copy();
}
globals->_actionMap = _actionMap->Copy();
std::copy(_keybindingsWarnings.begin(), _keybindingsWarnings.end(), std::back_inserter(globals->_keybindingsWarnings));
if (_colorSchemes)
@@ -143,15 +138,6 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
}
}
if (_commands)
{
for (auto kv : _commands)
{
const auto commandImpl{ winrt::get_self<Command>(kv.Value()) };
globals->_commands.Insert(kv.Key(), *commandImpl->Copy());
}
}
// Globals only ever has 1 parent
FAIL_FAST_IF(_parents.size() > 1);
for (auto parent : _parents)
@@ -232,9 +218,9 @@ std::optional<winrt::hstring> GlobalAppSettings::_getUnparsedDefaultProfileImpl(
}
#pragma endregion
winrt::Microsoft::Terminal::Settings::Model::KeyMapping GlobalAppSettings::KeyMap() const noexcept
winrt::Microsoft::Terminal::Settings::Model::ActionMap GlobalAppSettings::ActionMap() const noexcept
{
return *_keymap;
return *_actionMap;
}
// Method Description:
@@ -326,7 +312,8 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
auto parseBindings = [this, &json](auto jsonKey) {
if (auto bindings{ json[JsonKey(jsonKey)] })
{
auto warnings = _keymap->LayerJson(bindings);
auto warnings = _actionMap->LayerJson(bindings);
// It's possible that the user provided keybindings have some warnings
// in them - problems that we should alert the user to, but we can
// recover from. Most of these warnings cannot be detected later in the
@@ -334,16 +321,6 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
// warnings generated from parsing these keybindings, add them to our
// list of warnings.
_keybindingsWarnings.insert(_keybindingsWarnings.end(), warnings.begin(), warnings.end());
// Now parse the array again, but this time as a list of commands.
warnings = implementation::Command::LayerJson(_commands, bindings);
// We cannot add all warnings, as some of them were already populated while parsing key mapping.
// Hence let's cherry-pick the ones relevant for command parsing.
if (std::count(warnings.begin(), warnings.end(), SettingsLoadWarnings::FailedToParseSubCommands))
{
_keybindingsWarnings.push_back(SettingsLoadWarnings::FailedToParseSubCommands);
}
}
};
parseBindings(LegacyKeybindingsKey);
@@ -380,11 +357,6 @@ std::vector<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> G
return _keybindingsWarnings;
}
winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::Command> GlobalAppSettings::Commands() noexcept
{
return _commands.GetView();
}
// Method Description:
// - Create a new serialized JsonObject from an instance of this class
// Arguments:

View File

@@ -18,7 +18,7 @@ Author(s):
#include "GlobalAppSettings.g.h"
#include "IInheritable.h"
#include "KeyMapping.h"
#include "ActionMap.h"
#include "Command.h"
#include "ColorScheme.h"
@@ -42,7 +42,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void AddColorScheme(const Model::ColorScheme& scheme);
void RemoveColorScheme(hstring schemeName);
Model::KeyMapping KeyMap() const noexcept;
Model::ActionMap ActionMap() const noexcept;
static com_ptr<GlobalAppSettings> FromJson(const Json::Value& json);
void LayerJson(const Json::Value& json);
@@ -51,8 +51,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
std::vector<SettingsLoadWarnings> KeybindingsWarnings() const;
Windows::Foundation::Collections::IMapView<hstring, Model::Command> Commands() noexcept;
// These are implemented manually to handle the string/GUID exchange
// by higher layers in the app.
void DefaultProfile(const guid& defaultProfile) noexcept;
@@ -97,11 +95,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
std::optional<hstring> _UnparsedDefaultProfile{ std::nullopt };
bool _validDefaultProfile;
com_ptr<KeyMapping> _keymap;
com_ptr<implementation::ActionMap> _actionMap;
std::vector<SettingsLoadWarnings> _keybindingsWarnings;
Windows::Foundation::Collections::IMap<hstring, Model::ColorScheme> _colorSchemes;
Windows::Foundation::Collections::IMap<hstring, Model::Command> _commands;
std::optional<hstring> _getUnparsedDefaultProfileImpl() const;
static bool _getDefaultDebugFeaturesValue();

View File

@@ -4,8 +4,7 @@
#include "IInheritable.idl.h"
import "ColorScheme.idl";
import "KeyMapping.idl";
import "Command.idl";
import "ActionMap.idl";
namespace Microsoft.Terminal.Settings.Model
{
@@ -73,8 +72,6 @@ namespace Microsoft.Terminal.Settings.Model
void AddColorScheme(ColorScheme scheme);
void RemoveColorScheme(String schemeName);
KeyMapping KeyMap();
Windows.Foundation.Collections.IMapView<String, Command> Commands();
ActionMap ActionMap { get; };
}
}

View File

@@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/*++
Module Name:
- HashUtils.h
Abstract:
- This module is used for hashing data consistently
Author(s):
- Carlos Zamora (CaZamor) 15-Apr-2021
Revision History:
- N/A
--*/
#pragma once
namespace Microsoft::Terminal::Settings::Model::HashUtils
{
// This is a helper template function for hashing multiple variables in conjunction to each other.
template<typename T>
constexpr size_t HashProperty(const T& val)
{
std::hash<T> hashFunc;
return hashFunc(val);
}
template<typename T, typename... Args>
constexpr size_t HashProperty(const T& val, Args&&... more)
{
return HashProperty(val) ^ HashProperty(std::forward<Args>(more)...);
}
template<>
constexpr size_t HashProperty(const til::color& val)
{
return HashProperty(val.a, val.r, val.g, val.b);
}
#ifdef WINRT_Windows_Foundation_H
template<typename T>
constexpr size_t HashProperty(const winrt::Windows::Foundation::IReference<T>& val)
{
return val ? HashProperty(val.Value()) : 0;
}
#endif
#ifdef WINRT_Windows_UI_H
template<>
constexpr size_t HashProperty(const winrt::Windows::UI::Color& val)
{
return HashProperty(til::color{ val });
}
#endif
#ifdef WINRT_Microsoft_Terminal_Core_H
template<>
constexpr size_t HashProperty(const winrt::Microsoft::Terminal::Core::Color& val)
{
return HashProperty(til::color{ val });
}
#endif
};

View File

@@ -7,6 +7,7 @@
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
using namespace Microsoft::Terminal::Settings::Model::JsonUtils;
static constexpr std::wstring_view CTRL_KEY{ L"ctrl" };
static constexpr std::wstring_view SHIFT_KEY{ L"shift" };
@@ -104,14 +105,12 @@ static const std::unordered_map<std::wstring_view, int32_t> vkeyNamePairs {
// - hstr: the string to parse into a keychord.
// Return Value:
// - a newly constructed KeyChord
KeyChord KeyChordSerialization::FromString(const winrt::hstring& hstr)
static KeyChord _FromString(const std::wstring_view& wstr)
{
std::wstring wstr{ hstr };
// Split the string on '+'
std::wstring temp;
std::vector<std::wstring> parts;
std::wstringstream wss(wstr);
std::wstringstream wss(wstr.data());
while (std::getline(wss, temp, L'+'))
{
@@ -220,8 +219,13 @@ KeyChord KeyChordSerialization::FromString(const winrt::hstring& hstr)
// names listed in the vkeyNamePairs vector above, or is one of 0-9a-z.
// Return Value:
// - a string which is an equivalent serialization of this object.
winrt::hstring KeyChordSerialization::ToString(const KeyChord& chord)
static std::wstring _ToString(const KeyChord& chord)
{
if (!chord)
{
return {};
}
bool serializedSuccessfully = false;
const auto modifiers = chord.Modifiers();
const auto vkey = chord.Vkey();
@@ -292,5 +296,57 @@ winrt::hstring KeyChordSerialization::ToString(const KeyChord& chord)
buffer = L"";
}
return winrt::hstring{ buffer };
return buffer;
}
KeyChord KeyChordSerialization::FromString(const winrt::hstring& hstr)
{
return _FromString(hstr);
}
winrt::hstring KeyChordSerialization::ToString(const KeyChord& chord)
{
return hstring{ _ToString(chord) };
}
KeyChord ConversionTrait<KeyChord>::FromJson(const Json::Value& json)
{
try
{
std::string keyChordText;
if (json.isString())
{
// "keys": "ctrl+c"
keyChordText = json.asString();
}
else if (json.isArray() && json.size() == 1)
{
// "keys": [ "ctrl+c" ]
keyChordText = json[0].asString();
}
else
{
throw winrt::hresult_invalid_argument{};
}
return _FromString(til::u8u16(keyChordText));
}
catch (...)
{
return nullptr;
}
}
bool ConversionTrait<KeyChord>::CanConvert(const Json::Value& json)
{
return json.isString() || (json.isArray() && json.size() == 1);
}
Json::Value ConversionTrait<KeyChord>::ToJson(const KeyChord& val)
{
return til::u16u8(_ToString(val));
}
std::string ConversionTrait<KeyChord>::TypeDescription() const
{
return "key chord";
}

View File

@@ -4,6 +4,7 @@
#pragma once
#include "KeyChordSerialization.g.h"
#include "JsonUtils.h"
#include "../inc/cppwinrt_utils.h"
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
@@ -17,6 +18,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
};
}
template<>
struct Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<winrt::Microsoft::Terminal::Control::KeyChord>
{
winrt::Microsoft::Terminal::Control::KeyChord FromJson(const Json::Value& json);
bool CanConvert(const Json::Value& json);
Json::Value ToJson(const winrt::Microsoft::Terminal::Control::KeyChord& val);
std::string TypeDescription() const;
};
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
// C++/WinRT generates a constructor even though one is not specified in the IDL

View File

@@ -1,144 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "KeyMapping.h"
#include "KeyChordSerialization.h"
#include "ActionAndArgs.h"
#include "KeyMapping.g.cpp"
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Control;
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
winrt::com_ptr<KeyMapping> KeyMapping::Copy() const
{
auto keymap{ winrt::make_self<KeyMapping>() };
std::for_each(_keyShortcuts.begin(), _keyShortcuts.end(), [keymap](auto& kv) {
const auto actionAndArgsImpl{ winrt::get_self<ActionAndArgs>(kv.second) };
keymap->_keyShortcuts[kv.first] = *actionAndArgsImpl->Copy();
});
return keymap;
}
Microsoft::Terminal::Settings::Model::ActionAndArgs KeyMapping::TryLookup(KeyChord const& chord) const
{
const auto result = _keyShortcuts.find(chord);
if (result != _keyShortcuts.end())
{
return result->second;
}
return nullptr;
}
uint64_t KeyMapping::Size() const
{
return _keyShortcuts.size();
}
void KeyMapping::SetKeyBinding(const Microsoft::Terminal::Settings::Model::ActionAndArgs& actionAndArgs,
const KeyChord& chord)
{
// if the chord is already mapped - clear the mapping
if (_keyShortcuts.find(chord) != _keyShortcuts.end())
{
ClearKeyBinding(chord);
}
_keyShortcuts[chord] = actionAndArgs;
_keyShortcutsByInsertionOrder.push_back(std::make_pair(chord, actionAndArgs));
}
// Method Description:
// - Remove the action that's bound to a particular KeyChord.
// Arguments:
// - chord: the keystroke to remove the action for.
// Return Value:
// - <none>
void KeyMapping::ClearKeyBinding(const KeyChord& chord)
{
_keyShortcuts.erase(chord);
KeyChordEquality keyChordEquality;
_keyShortcutsByInsertionOrder.erase(std::remove_if(_keyShortcutsByInsertionOrder.begin(), _keyShortcutsByInsertionOrder.end(), [keyChordEquality, chord](const auto& keyBinding) {
return keyChordEquality(keyBinding.first, chord);
}),
_keyShortcutsByInsertionOrder.end());
}
KeyChord KeyMapping::GetKeyBindingForAction(Microsoft::Terminal::Settings::Model::ShortcutAction const& action)
{
for (auto it = _keyShortcutsByInsertionOrder.rbegin(); it != _keyShortcutsByInsertionOrder.rend(); ++it)
{
const auto& kv = *it;
if (kv.second.Action() == action)
{
return kv.first;
}
}
return { nullptr };
}
// Method Description:
// - Lookup the keychord bound to a particular combination of ShortcutAction
// and IActionArgs. This enables searching no only for the binding of a
// particular ShortcutAction, but also a particular set of values for
// arguments to that action.
// If several bindings might match the lookup, prefers the one that was added last.
// Arguments:
// - actionAndArgs: The ActionAndArgs to lookup the keybinding for.
// Return Value:
// - The bound keychord, if this ActionAndArgs is bound to a key, otherwise nullptr.
KeyChord KeyMapping::GetKeyBindingForActionWithArgs(Microsoft::Terminal::Settings::Model::ActionAndArgs const& actionAndArgs)
{
if (actionAndArgs == nullptr)
{
return { nullptr };
}
for (auto it = _keyShortcutsByInsertionOrder.rbegin(); it != _keyShortcutsByInsertionOrder.rend(); ++it)
{
const auto& kv = *it;
const auto action = kv.second.Action();
const auto args = kv.second.Args();
const auto actionMatched = action == actionAndArgs.Action();
const auto argsMatched = args ? args.Equals(actionAndArgs.Args()) : args == actionAndArgs.Args();
if (actionMatched && argsMatched)
{
return kv.first;
}
}
return { nullptr };
}
// Method Description:
// - Takes the KeyModifier flags from Terminal and maps them to the WinRT types which are used by XAML
// Return Value:
// - a Windows::System::VirtualKeyModifiers object with the flags of which modifiers used.
Windows::System::VirtualKeyModifiers KeyMapping::ConvertVKModifiers(KeyModifiers modifiers)
{
Windows::System::VirtualKeyModifiers keyModifiers = Windows::System::VirtualKeyModifiers::None;
if (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Control;
}
if (WI_IsFlagSet(modifiers, KeyModifiers::Shift))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Shift;
}
if (WI_IsFlagSet(modifiers, KeyModifiers::Alt))
{
// note: Menu is the Alt VK_MENU
keyModifiers |= Windows::System::VirtualKeyModifiers::Menu;
}
if (WI_IsFlagSet(modifiers, KeyModifiers::Windows))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Windows;
}
return keyModifiers;
}
}

View File

@@ -1,80 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- KeyMapping.h
Abstract:
- A mapping of key chords to actions. Includes (de)serialization logic.
Author(s):
- Carlos Zamora - September 2020
--*/
#pragma once
#include "KeyMapping.g.h"
#include "ActionArgs.h"
#include "../inc/cppwinrt_utils.h"
// fwdecl unittest classes
namespace SettingsModelLocalTests
{
class DeserializationTests;
class KeyBindingsTests;
class TestUtils;
}
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct KeyChordHash
{
std::size_t operator()(const Control::KeyChord& key) const
{
std::hash<int32_t> keyHash;
std::hash<Control::KeyModifiers> modifiersHash;
std::size_t hashedKey = keyHash(key.Vkey());
std::size_t hashedMods = modifiersHash(key.Modifiers());
return hashedKey ^ hashedMods;
}
};
struct KeyChordEquality
{
bool operator()(const Control::KeyChord& lhs, const Control::KeyChord& rhs) const
{
return lhs.Modifiers() == rhs.Modifiers() && lhs.Vkey() == rhs.Vkey();
}
};
struct KeyMapping : KeyMappingT<KeyMapping>
{
KeyMapping() = default;
com_ptr<KeyMapping> Copy() const;
Model::ActionAndArgs TryLookup(Control::KeyChord const& chord) const;
uint64_t Size() const;
void SetKeyBinding(Model::ActionAndArgs const& actionAndArgs,
Control::KeyChord const& chord);
void ClearKeyBinding(Control::KeyChord const& chord);
Control::KeyChord GetKeyBindingForAction(Model::ShortcutAction const& action);
Control::KeyChord GetKeyBindingForActionWithArgs(Model::ActionAndArgs const& actionAndArgs);
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(Control::KeyModifiers modifiers);
// Defined in KeyMappingSerialization.cpp
std::vector<Model::SettingsLoadWarnings> LayerJson(const Json::Value& json);
Json::Value ToJson();
private:
std::unordered_map<Control::KeyChord, Model::ActionAndArgs, KeyChordHash, KeyChordEquality> _keyShortcuts;
std::vector<std::pair<Control::KeyChord, Model::ActionAndArgs>> _keyShortcutsByInsertionOrder;
friend class SettingsModelLocalTests::DeserializationTests;
friend class SettingsModelLocalTests::KeyBindingsTests;
friend class SettingsModelLocalTests::TestUtils;
};
}

View File

@@ -1,39 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "AllShortcutActions.h"
import "ActionArgs.idl";
namespace Microsoft.Terminal.Settings.Model
{
enum ShortcutAction
{
Invalid = 0,
// When adding a new action, add them to AllShortcutActions.h!
#define ON_ALL_ACTIONS(action) action,
ALL_SHORTCUT_ACTIONS
#undef ON_ALL_ACTIONS
};
[default_interface] runtimeclass ActionAndArgs {
ActionAndArgs();
ActionAndArgs(ShortcutAction action, IActionArgs args);
IActionArgs Args;
ShortcutAction Action;
};
[default_interface] runtimeclass KeyMapping
{
ActionAndArgs TryLookup(Microsoft.Terminal.Control.KeyChord chord);
UInt64 Size();
void SetKeyBinding(ActionAndArgs actionAndArgs, Microsoft.Terminal.Control.KeyChord chord);
void ClearKeyBinding(Microsoft.Terminal.Control.KeyChord chord);
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action);
Microsoft.Terminal.Control.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs);
}
}

View File

@@ -1,162 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// - A couple helper functions for serializing/deserializing a KeyMapping
// to/from json.
//
// Author(s):
// - Mike Griese - May 2019
#include "pch.h"
#include "KeyMapping.h"
#include "ActionAndArgs.h"
#include "KeyChordSerialization.h"
#include "JsonUtils.h"
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::Settings::Model;
static constexpr std::string_view KeysKey{ "keys" };
static constexpr std::string_view CommandKey{ "command" };
static constexpr std::string_view ActionKey{ "action" };
// Function Description:
// - Small helper to create a json value serialization of a single
// KeyBinding->Action mapping.
// {
// keys:[String],
// command:String
// }
// Arguments:
// - chord: The KeyChord to serialize
// - actionName: the name of the ShortcutAction to use with this KeyChord
// Return Value:
// - a Json::Value which is an equivalent serialization of this object.
static Json::Value _ShortcutAsJsonObject(const KeyChord& chord,
const std::string_view actionName)
{
const auto keyString = KeyChordSerialization::ToString(chord);
if (keyString == L"")
{
return nullptr;
}
Json::Value jsonObject;
Json::Value keysArray;
keysArray.append(winrt::to_string(keyString));
jsonObject[JsonKey(KeysKey)] = keysArray;
jsonObject[JsonKey(CommandKey)] = actionName.data();
return jsonObject;
}
// Method Description:
// - Serialize this KeyMapping to a json array of objects. Each object in
// the array represents a single keybinding, mapping a KeyChord to a
// ShortcutAction.
// Return Value:
// - a Json::Value which is an equivalent serialization of this object.
Json::Value winrt::Microsoft::Terminal::Settings::Model::implementation::KeyMapping::ToJson()
{
Json::Value bindingsArray;
// Iterate over all the possible actions in the names list, and see if
// it has a binding.
for (auto& actionName : ActionAndArgs::ActionKeyNamesMap)
{
const auto searchedForName = actionName.first;
const auto searchedForAction = actionName.second;
if (const auto chord{ GetKeyBindingForAction(searchedForAction) })
{
if (const auto serialization{ _ShortcutAsJsonObject(chord, searchedForName) })
{
bindingsArray.append(serialization);
}
}
}
return bindingsArray;
}
// Method Description:
// - Deserialize a KeyMapping from the key mappings that are in the array
// `json`. The json array should contain an array of objects with both a
// `command` string and a `keys` array, where `command` is one of the names
// listed in `ActionAndArgs::ActionKeyNamesMap`, and `keys` is an array of
// keypresses. Currently, the array should contain a single string, which can
// be deserialized into a KeyChord.
// - Applies the deserialized keybindings to the provided `bindings` object. If
// a key chord in `json` is already bound to an action, that chord will be
// overwritten with the new action. If a chord is bound to `null` or
// `"unbound"`, then we'll clear the keybinding from the existing keybindings.
// Arguments:
// - json: an array of Json::Value's to deserialize into our _keyShortcuts mapping.
std::vector<SettingsLoadWarnings> winrt::Microsoft::Terminal::Settings::Model::implementation::KeyMapping::LayerJson(const Json::Value& json)
{
// It's possible that the user provided keybindings have some warnings in
// them - problems that we should alert the user to, but we can recover
// from. Most of these warnings cannot be detected later in the Validate
// settings phase, so we'll collect them now.
std::vector<SettingsLoadWarnings> warnings;
for (const auto& value : json)
{
if (!value.isObject())
{
continue;
}
const auto commandVal = value[JsonKey(CommandKey)];
const auto keys = value[JsonKey(KeysKey)];
if (keys)
{
const auto validString = keys.isString();
const auto validArray = keys.isArray() && keys.size() == 1;
// GH#4239 - If the user provided more than one key
// chord to a "keys" array, warn the user here.
// TODO: GH#1334 - remove this check.
if (keys.isArray() && keys.size() > 1)
{
warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord);
}
if (!validString && !validArray)
{
continue;
}
const auto keyChordString = keys.isString() ? winrt::to_hstring(keys.asString()) : winrt::to_hstring(keys[0].asString());
// If the action was null, "unbound", or something we didn't
// understand, this will return nullptr.
auto actionAndArgs = ActionAndArgs::FromJson(commandVal, warnings);
// Try parsing the chord
try
{
const auto chord = KeyChordSerialization::FromString(keyChordString);
// If we couldn't find the action they want to set the chord to,
// or the action was `null` or `"unbound"`, just clear out the
// keybinding. Otherwise, set the keybinding to the action we
// found.
if (actionAndArgs)
{
SetKeyBinding(*actionAndArgs, chord);
}
else
{
ClearKeyBinding(chord);
}
}
catch (...)
{
continue;
}
}
}
return warnings;
}

View File

@@ -26,6 +26,9 @@
<ClInclude Include="ActionAndArgs.h">
<DependentUpon>ActionArgs.idl</DependentUpon>
</ClInclude>
<ClInclude Include="ActionMap.h">
<DependentUpon>ActionMap.idl</DependentUpon>
</ClInclude>
<ClInclude Include="AzureCloudShellGenerator.h" />
<ClInclude Include="CascadiaSettings.h">
<DependentUpon>CascadiaSettings.idl</DependentUpon>
@@ -43,12 +46,10 @@
<ClInclude Include="IInheritable.h" />
<ClInclude Include="IDynamicProfileGenerator.h" />
<ClInclude Include="JsonUtils.h" />
<ClInclude Include="HashUtils.h" />
<ClInclude Include="KeyChordSerialization.h">
<DependentUpon>KeyChordSerialization.idl</DependentUpon>
</ClInclude>
<ClInclude Include="KeyMapping.h">
<DependentUpon>KeyMapping.idl</DependentUpon>
</ClInclude>
<ClInclude Include="PowershellCoreProfileGenerator.h" />
<ClInclude Include="Profile.h">
<DependentUpon>Profile.idl</DependentUpon>
@@ -84,6 +85,12 @@
<ClCompile Include="ActionArgs.cpp">
<DependentUpon>ActionArgs.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ActionMap.cpp">
<DependentUpon>ActionMap.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ActionMapSerialization.cpp">
<DependentUpon>ActionMap.idl</DependentUpon>
</ClCompile>
<ClCompile Include="AzureCloudShellGenerator.cpp" />
<ClCompile Include="CascadiaSettings.cpp">
<DependentUpon>CascadiaSettings.idl</DependentUpon>
@@ -104,12 +111,6 @@
<ClCompile Include="KeyChordSerialization.cpp">
<DependentUpon>KeyChordSerialization.idl</DependentUpon>
</ClCompile>
<ClCompile Include="KeyMapping.cpp">
<DependentUpon>KeyMapping.idl</DependentUpon>
</ClCompile>
<ClCompile Include="KeyMappingSerialization.cpp">
<DependentUpon>KeyMapping.idl</DependentUpon>
</ClCompile>
<ClCompile Include="PowershellCoreProfileGenerator.cpp" />
<ClCompile Include="Profile.cpp">
<DependentUpon>Profile.idl</DependentUpon>
@@ -134,12 +135,12 @@
<!-- ========================= idl Files ======================== -->
<ItemGroup>
<Midl Include="ActionArgs.idl" />
<Midl Include="ActionMap.idl" />
<Midl Include="CascadiaSettings.idl" />
<Midl Include="ColorScheme.idl" />
<Midl Include="Command.idl" />
<Midl Include="GlobalAppSettings.idl" />
<Midl Include="IconPathConverter.idl" />
<Midl Include="KeyMapping.idl" />
<Midl Include="Profile.idl" />
<Midl Include="EnumMappings.idl" />
<Midl Include="TerminalSettings.idl" />

View File

@@ -67,16 +67,17 @@
</ItemGroup>
<ItemGroup>
<Midl Include="ActionArgs.idl" />
<Midl Include="ActionMap.idl" />
<Midl Include="ColorScheme.idl" />
<Midl Include="Command.idl" />
<Midl Include="GlobalAppSettings.idl" />
<Midl Include="KeyMapping.idl" />
<Midl Include="Profile.idl" />
<Midl Include="TerminalWarnings.idl" />
<Midl Include="CascadiaSettings.idl" />
<Midl Include="KeyChordSerialization.idl" />
<Midl Include="EnumMappings.idl" />
<Midl Include="IconPathConverter.idl" />
<Midl Include="TerminalSettings.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@@ -89,4 +90,4 @@
<UniqueIdentifier>{81a6314f-aa5b-4533-a499-13bc3a5c4af0}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>

View File

@@ -22,6 +22,7 @@
<!-- Only put headers for winrt types in here. Don't put other header files
in here - put them in the lib's vcxproj instead! -->
<ClInclude Include="../ActionArgs.h" />
<ClInclude Include="../ActionMap.h" />
<ClInclude Include="../CascadiaSettings.h" />
<ClInclude Include="../ColorScheme.h" />
<ClInclude Include="../Command.h" />

View File

@@ -71,6 +71,13 @@
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<!--
SettingsModelLib contains a DllMain that we need to force the use of.
If you don't have this, then you'll see an error like
"(init.obj) : error LNK2005: DllMain already defined in MSVCRTD.lib(dll_dllmain_stub.obj)"
-->
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain</AdditionalOptions>
</Link>
</ItemDefinitionGroup>