mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-14 10:11:00 +00:00
Compare commits
49 Commits
dev/miniks
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2fe05c990 | ||
|
|
93b1e6e242 | ||
|
|
8bcf0a936f | ||
|
|
50a78bb7ea | ||
|
|
ea1bb2ed93 | ||
|
|
08df4fc27a | ||
|
|
6f0f245dd0 | ||
|
|
c65de31172 | ||
|
|
2bd1e398bd | ||
|
|
a09989749a | ||
|
|
2d09dfd48b | ||
|
|
7936605cc9 | ||
|
|
edf79b281a | ||
|
|
c55d9cd62d | ||
|
|
191b0d288a | ||
|
|
1299a839bd | ||
|
|
6fabc4abb7 | ||
|
|
5622f230a8 | ||
|
|
761bee80c0 | ||
|
|
26778440b3 | ||
|
|
7335232cda | ||
|
|
7c7db79782 | ||
|
|
a9c9714295 | ||
|
|
52d6c03e64 | ||
|
|
e44dc2bcf8 | ||
|
|
38803d7d05 | ||
|
|
6d4c44f3a4 | ||
|
|
b1a5604b55 | ||
|
|
4f8acb4b9f | ||
|
|
94215535cd | ||
|
|
286af380c9 | ||
|
|
9513d543b7 | ||
|
|
ecc6c89dde | ||
|
|
f57ed0518a | ||
|
|
084b48a751 | ||
|
|
90a78d65ac | ||
|
|
bdbf40451e | ||
|
|
584e4498ee | ||
|
|
713550d56c | ||
|
|
9409e851d0 | ||
|
|
64489b1ec1 | ||
|
|
c1463bdbf0 | ||
|
|
a12a6285f5 | ||
|
|
f8227e6fa2 | ||
|
|
8585bc6bde | ||
|
|
9a0b6e3b69 | ||
|
|
a621a6fabb | ||
|
|
cd9e854553 | ||
|
|
ca6b54e652 |
479826
.github/actions/spell-check/dictionary/dictionary.txt
vendored
479826
.github/actions/spell-check/dictionary/dictionary.txt
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
powf
|
||||
sqrtf
|
||||
isnan
|
||||
@@ -1,7 +0,0 @@
|
||||
mfcribbon
|
||||
microsoft
|
||||
microsoftonline
|
||||
osgvsowi
|
||||
powershell
|
||||
tdbuildteamid
|
||||
visualstudio
|
||||
61
.github/actions/spell-check/excludes.txt
vendored
61
.github/actions/spell-check/excludes.txt
vendored
@@ -1,61 +0,0 @@
|
||||
(?:^|/)dirs$
|
||||
(?:^|/)go\.mod$
|
||||
(?:^|/)go\.sum$
|
||||
(?:^|/)package-lock\.json$
|
||||
(?:^|/)sources(?:|\.dep)$
|
||||
SUMS$
|
||||
\.ai$
|
||||
\.bmp$
|
||||
\.cer$
|
||||
\.class$
|
||||
\.crl$
|
||||
\.crt$
|
||||
\.csr$
|
||||
\.dll$
|
||||
\.DS_Store$
|
||||
\.eot$
|
||||
\.eps$
|
||||
\.exe$
|
||||
\.gif$
|
||||
\.graffle$
|
||||
\.gz$
|
||||
\.icns$
|
||||
\.ico$
|
||||
\.jar$
|
||||
\.jpeg$
|
||||
\.jpg$
|
||||
\.key$
|
||||
\.lib$
|
||||
\.lock$
|
||||
\.map$
|
||||
\.min\..
|
||||
\.mp3$
|
||||
\.mp4$
|
||||
\.otf$
|
||||
\.pbxproj$
|
||||
\.pdf$
|
||||
\.pem$
|
||||
\.png$
|
||||
\.psd$
|
||||
\.runsettings$
|
||||
\.sig$
|
||||
\.so$
|
||||
\.svg$
|
||||
\.svgz$
|
||||
\.tar$
|
||||
\.tgz$
|
||||
\.ttf$
|
||||
\.woff
|
||||
\.xcf$
|
||||
\.xls
|
||||
\.xpm$
|
||||
\.yml$
|
||||
\.zip$
|
||||
^dep/
|
||||
^doc/reference/UTF8-torture-test\.txt$
|
||||
^src/interactivity/onecore/BgfxEngine\.
|
||||
^src/renderer/wddmcon/WddmConRenderer\.
|
||||
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^\.github/actions/spell-check/
|
||||
^\.gitignore$
|
||||
@@ -1,7 +0,0 @@
|
||||
https://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]*
|
||||
(?:0[Xx]|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]?\b
|
||||
\{[0-9A-FA-F]{8}-(?:[0-9A-FA-F]{4}-){3}[0-9A-FA-F]{12}\}
|
||||
\d+x\d+Logo
|
||||
Scro\&ll
|
||||
# selectionInput.cpp
|
||||
:\\windows\\syste\b
|
||||
@@ -1,7 +0,0 @@
|
||||
The contents of each `.txt` file in this directory are merged together.
|
||||
|
||||
* [alphabet](alphabet.txt) is a sample for alphabet related items
|
||||
* [web](web.txt) is a sample for web/html related items
|
||||
* [whitelist](whitelist.txt) is the main whitelist -- there is nothing
|
||||
particularly special about the file name (beyond the extension which is
|
||||
important).
|
||||
@@ -1,3 +0,0 @@
|
||||
http
|
||||
td
|
||||
www
|
||||
15
.github/actions/spelling/README.md
vendored
Normal file
15
.github/actions/spelling/README.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# check-spelling/check-spelling configuration
|
||||
|
||||
File | Purpose | Format | Info
|
||||
-|-|-|-
|
||||
[allow/*.txt](allow/) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow)
|
||||
[reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject)
|
||||
[excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
|
||||
[patterns/*.txt](patterns/) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns)
|
||||
[line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[expect/*.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect)
|
||||
[advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice)
|
||||
|
||||
Note: you can replace any of these files with a directory by the same name (minus the suffix)
|
||||
and then include multiple files inside that directory (with that suffix) to merge multiple files together.
|
||||
48
.github/actions/spelling/advice.md
vendored
Normal file
48
.github/actions/spelling/advice.md
vendored
Normal 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>
|
||||
@@ -1,6 +1,6 @@
|
||||
# Dictionaries are lists of words to accept unconditionally
|
||||
# Allow files are lists of words to accept unconditionally
|
||||
|
||||
While check spelling will complain about a whitelisted word
|
||||
While check spelling will complain about an expected word
|
||||
which is no longer present, you can include things here even if
|
||||
they are not otherwise present in the repository.
|
||||
|
||||
@@ -8,13 +8,14 @@ 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 |
|
||||
| [Fonts](fonts.txt) | Font names |
|
||||
| [Names](names.txt) | Names of people |
|
||||
| [Colors](colors.txt) | Names of color |
|
||||
108
.github/actions/spelling/allow/allow.txt
vendored
Normal file
108
.github/actions/spelling/allow/allow.txt
vendored
Normal 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
|
||||
248
.github/actions/spelling/allow/apis.txt
vendored
Normal file
248
.github/actions/spelling/allow/apis.txt
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
ACCEPTFILES
|
||||
ACCESSDENIED
|
||||
acl
|
||||
aclapi
|
||||
alignas
|
||||
alignof
|
||||
APPLYTOSUBMENUS
|
||||
appxrecipe
|
||||
bitfield
|
||||
bitfields
|
||||
BUILDBRANCH
|
||||
BUILDMSG
|
||||
BUILDNUMBER
|
||||
BYCOMMAND
|
||||
BYPOSITION
|
||||
charconv
|
||||
CLASSNOTAVAILABLE
|
||||
CLOSEAPP
|
||||
cmdletbinding
|
||||
COLORPROPERTY
|
||||
colspan
|
||||
COMDLG
|
||||
commandlinetoargv
|
||||
comparand
|
||||
cstdint
|
||||
CXICON
|
||||
CYICON
|
||||
Dacl
|
||||
dataobject
|
||||
dcomp
|
||||
DERR
|
||||
dlldata
|
||||
DNE
|
||||
DONTADDTORECENT
|
||||
DWMSBT
|
||||
DWMWA
|
||||
DWMWA
|
||||
DWORDLONG
|
||||
endfor
|
||||
ENDSESSION
|
||||
enumset
|
||||
environstrings
|
||||
EXPCMDFLAGS
|
||||
EXPCMDSTATE
|
||||
filetime
|
||||
FILTERSPEC
|
||||
FORCEFILESYSTEM
|
||||
FORCEMINIMIZE
|
||||
frac
|
||||
fullkbd
|
||||
futex
|
||||
GETDESKWALLPAPER
|
||||
GETHIGHCONTRAST
|
||||
GETMOUSEHOVERTIME
|
||||
Hashtable
|
||||
HIGHCONTRASTON
|
||||
HIGHCONTRASTW
|
||||
hotkeys
|
||||
href
|
||||
hrgn
|
||||
HTCLOSE
|
||||
hwinsta
|
||||
HWINSTA
|
||||
IActivation
|
||||
IApp
|
||||
IAppearance
|
||||
IAsync
|
||||
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
|
||||
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
|
||||
ptstr
|
||||
QUERYENDSESSION
|
||||
rcx
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
ROOTOWNER
|
||||
roundf
|
||||
RSHIFT
|
||||
SACL
|
||||
schandle
|
||||
semver
|
||||
serializer
|
||||
SETVERSION
|
||||
SHELLEXECUTEINFOW
|
||||
shobjidl
|
||||
SHOWHIDE
|
||||
SHOWMINIMIZED
|
||||
SHOWTIP
|
||||
SINGLEUSE
|
||||
SIZENS
|
||||
smoothstep
|
||||
snprintf
|
||||
spsc
|
||||
sregex
|
||||
SRWLOC
|
||||
SRWLOCK
|
||||
STDCPP
|
||||
STDMETHOD
|
||||
strchr
|
||||
strcpy
|
||||
streambuf
|
||||
strtoul
|
||||
Stubless
|
||||
Subheader
|
||||
Subpage
|
||||
syscall
|
||||
SYSTEMBACKDROP
|
||||
TABROW
|
||||
TASKBARCREATED
|
||||
TBPF
|
||||
THEMECHANGED
|
||||
tlg
|
||||
TME
|
||||
tmp
|
||||
tmpdir
|
||||
tolower
|
||||
toupper
|
||||
TRACKMOUSEEVENT
|
||||
TTask
|
||||
TVal
|
||||
UChar
|
||||
UFIELD
|
||||
ULARGE
|
||||
UOI
|
||||
UPDATEINIFILE
|
||||
userenv
|
||||
USEROBJECTFLAGS
|
||||
Viewbox
|
||||
virtualalloc
|
||||
wcsstr
|
||||
wcstoui
|
||||
winmain
|
||||
winsta
|
||||
winstamin
|
||||
wmemcmp
|
||||
wpc
|
||||
WSF
|
||||
wsregex
|
||||
wwinmain
|
||||
xchg
|
||||
XDocument
|
||||
XElement
|
||||
xfacet
|
||||
xhash
|
||||
XIcon
|
||||
xiosbase
|
||||
xlocale
|
||||
xlocbuf
|
||||
xlocinfo
|
||||
xlocmes
|
||||
xlocmon
|
||||
xlocnum
|
||||
xloctime
|
||||
XMax
|
||||
xmemory
|
||||
XParse
|
||||
xpath
|
||||
xstddef
|
||||
xstring
|
||||
xtree
|
||||
xutility
|
||||
YIcon
|
||||
YMax
|
||||
117
.github/actions/spelling/allow/colors.txt
vendored
Normal file
117
.github/actions/spelling/allow/colors.txt
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
alice
|
||||
aliceblue
|
||||
antiquewhite
|
||||
blanchedalmond
|
||||
blueviolet
|
||||
burlywood
|
||||
cadetblue
|
||||
cornflowerblue
|
||||
cornsilk
|
||||
cyan
|
||||
darkblue
|
||||
darkcyan
|
||||
darkgoldenrod
|
||||
darkgray
|
||||
darkgreen
|
||||
darkgrey
|
||||
darkkhaki
|
||||
darkmagenta
|
||||
darkolivegreen
|
||||
darkorange
|
||||
darkorchid
|
||||
darkred
|
||||
darksalmon
|
||||
darkseagreen
|
||||
darkslateblue
|
||||
darkslategray
|
||||
darkslategrey
|
||||
darkturquoise
|
||||
darkviolet
|
||||
deeppink
|
||||
deepskyblue
|
||||
dimgray
|
||||
dimgrey
|
||||
dodgerblue
|
||||
firebrick
|
||||
floralwhite
|
||||
forestgreen
|
||||
gainsboro
|
||||
ghostwhite
|
||||
greenyellow
|
||||
hotpink
|
||||
indian
|
||||
indianred
|
||||
lavenderblush
|
||||
lawngreen
|
||||
lemonchiffon
|
||||
lightblue
|
||||
lightcoral
|
||||
lightcyan
|
||||
lightgoldenrod
|
||||
lightgoldenrodyellow
|
||||
lightgray
|
||||
lightgreen
|
||||
lightgrey
|
||||
lightpink
|
||||
lightsalmon
|
||||
lightseagreen
|
||||
lightskyblue
|
||||
lightslateblue
|
||||
lightslategray
|
||||
lightslategrey
|
||||
lightsteelblue
|
||||
lightyellow
|
||||
limegreen
|
||||
mediumaquamarine
|
||||
mediumblue
|
||||
mediumorchid
|
||||
mediumpurple
|
||||
mediumseagreen
|
||||
mediumslateblue
|
||||
mediumspringgreen
|
||||
mediumturquoise
|
||||
mediumvioletred
|
||||
midnightblue
|
||||
mintcream
|
||||
mistyrose
|
||||
navajo
|
||||
navajowhite
|
||||
navyblue
|
||||
oldlace
|
||||
olivedrab
|
||||
orangered
|
||||
palegoldenrod
|
||||
palegreen
|
||||
paleturquoise
|
||||
palevioletred
|
||||
papayawhip
|
||||
peachpuff
|
||||
peru
|
||||
powderblue
|
||||
rebecca
|
||||
rebeccapurple
|
||||
rosybrown
|
||||
royalblue
|
||||
saddlebrown
|
||||
sandybrown
|
||||
seagreen
|
||||
sienna
|
||||
skyblue
|
||||
slateblue
|
||||
slategray
|
||||
slategrey
|
||||
springgreen
|
||||
steelblue
|
||||
violetred
|
||||
webgray
|
||||
webgreen
|
||||
webgrey
|
||||
webmaroon
|
||||
webpurple
|
||||
whitesmoke
|
||||
xaroon
|
||||
xray
|
||||
xreen
|
||||
xrey
|
||||
xurple
|
||||
yellowgreen
|
||||
@@ -1,8 +1,10 @@
|
||||
Consolas
|
||||
emoji
|
||||
emojis
|
||||
Extralight
|
||||
Gabriola
|
||||
Iosevka
|
||||
MDL
|
||||
Monofur
|
||||
Segoe
|
||||
wght
|
||||
11
.github/actions/spelling/allow/math.txt
vendored
Normal file
11
.github/actions/spelling/allow/math.txt
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
atan
|
||||
CPrime
|
||||
HBar
|
||||
HPrime
|
||||
isnan
|
||||
LPrime
|
||||
LStep
|
||||
powf
|
||||
RSub
|
||||
sqrtf
|
||||
ULP
|
||||
85
.github/actions/spelling/allow/microsoft.txt
vendored
Normal file
85
.github/actions/spelling/allow/microsoft.txt
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
ACLs
|
||||
ADMINS
|
||||
advapi
|
||||
altform
|
||||
altforms
|
||||
appendwttlogging
|
||||
appx
|
||||
appxbundle
|
||||
appxerror
|
||||
appxmanifest
|
||||
ATL
|
||||
backplating
|
||||
bitmaps
|
||||
BOMs
|
||||
CPLs
|
||||
cpptools
|
||||
cppvsdbg
|
||||
CPRs
|
||||
cryptbase
|
||||
DACL
|
||||
DACLs
|
||||
defaultlib
|
||||
diffs
|
||||
disposables
|
||||
dotnetfeed
|
||||
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
|
||||
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
|
||||
wtt
|
||||
wttlog
|
||||
Xamarin
|
||||
@@ -1,55 +1,91 @@
|
||||
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
|
||||
kimwalisch
|
||||
KMehrain
|
||||
KODELIFE
|
||||
Kodelife
|
||||
Kourosh
|
||||
kowalczyk
|
||||
leonmsft
|
||||
Lepilleur
|
||||
lhecker
|
||||
lukesampson
|
||||
Macbook
|
||||
Manandhar
|
||||
masserano
|
||||
mbadolato
|
||||
Mehrain
|
||||
menger
|
||||
mgravell
|
||||
michaelniksa
|
||||
michkap
|
||||
migrie
|
||||
mikegr
|
||||
mikemaccana
|
||||
miloush
|
||||
miniksa
|
||||
niksa
|
||||
nvaccess
|
||||
nvda
|
||||
oising
|
||||
oldnewthing
|
||||
opengl
|
||||
osgwiki
|
||||
pabhojwa
|
||||
panos
|
||||
paulcam
|
||||
pauldotknopf
|
||||
PGP
|
||||
Pham
|
||||
Rincewind
|
||||
rprichard
|
||||
Schoonover
|
||||
shadertoy
|
||||
Shomnipotence
|
||||
simioni
|
||||
Somuah
|
||||
sonph
|
||||
sonpham
|
||||
stakx
|
||||
talo
|
||||
thereses
|
||||
Walisch
|
||||
WDX
|
||||
Wellons
|
||||
Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
Zamor
|
||||
Zamora
|
||||
zamora
|
||||
Zoey
|
||||
zorio
|
||||
Zverovich
|
||||
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
@@ -0,0 +1,523 @@
|
||||
# marker to ignore all code on line
|
||||
^.*/\* #no-spell-check-line \*/.*$
|
||||
# marker for ignoring a comment to the end of the line
|
||||
// #no-spell-check.*$
|
||||
|
||||
# patch hunk comments
|
||||
^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .*
|
||||
# git index header
|
||||
index [0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
|
||||
|
||||
# cid urls
|
||||
(['"])cid:.*?\g{-1}
|
||||
|
||||
# data url in parens
|
||||
\(data:[^)]*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\)
|
||||
# data url in quotes
|
||||
([`'"])data:.*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
|
||||
# data url
|
||||
data:[-a-zA-Z=;:/0-9+]*,\S*
|
||||
|
||||
# mailto urls
|
||||
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
|
||||
|
||||
# magnet urls
|
||||
magnet:[?=:\w]+
|
||||
|
||||
# magnet urls
|
||||
"magnet:[^"]+"
|
||||
|
||||
# obs:
|
||||
"obs:[^"]*"
|
||||
|
||||
# The `\b` here means a break, it's the fancy way to handle urls, but it makes things harder to read
|
||||
# In this examples content, I'm using a number of different ways to match things to show various approaches
|
||||
# asciinema
|
||||
\basciinema\.org/a/[0-9a-zA-Z]+
|
||||
|
||||
# apple
|
||||
\bdeveloper\.apple\.com/[-\w?=/]+
|
||||
# Apple music
|
||||
\bembed\.music\.apple\.com/fr/playlist/usr-share/[-\w.]+
|
||||
|
||||
# appveyor api
|
||||
\bci\.appveyor\.com/api/projects/status/[0-9a-z]+
|
||||
# appveyor project
|
||||
\bci\.appveyor\.com/project/(?:[^/\s"]*/){2}builds?/\d+/job/[0-9a-z]+
|
||||
|
||||
# Amazon
|
||||
|
||||
# Amazon
|
||||
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
|
||||
# AWS S3
|
||||
\b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS execute-api
|
||||
\b[0-9a-z]{10}\.execute-api\.[-0-9a-z]+\.amazonaws\.com\b
|
||||
# AWS ELB
|
||||
\b\w+\.[-0-9a-z]+\.elb\.amazonaws\.com\b
|
||||
# AWS SNS
|
||||
\bsns\.[-0-9a-z]+.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS VPC
|
||||
vpc-\w+
|
||||
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
|
||||
# YouTube music
|
||||
\bmusic\.youtube\.com/youtubei/v1/browse(?:[?&]\w+=[-a-zA-Z0-9?&=_]*)
|
||||
# YouTube tag
|
||||
<\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"]
|
||||
# YouTube image
|
||||
\bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]*
|
||||
# Google Accounts
|
||||
\baccounts.google.com/[-_/?=.:;+%&0-9a-zA-Z]*
|
||||
# Google Analytics
|
||||
\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
|
||||
# Google APIs
|
||||
\bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+
|
||||
# Google Storage
|
||||
\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
|
||||
# Google Calendar
|
||||
\bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+
|
||||
\w+\@group\.calendar\.google\.com\b
|
||||
# Google DataStudio
|
||||
\bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|)
|
||||
# The leading `/` here is as opposed to the `\b` above
|
||||
# ... a short way to match `https://` or `http://` since most urls have one of those prefixes
|
||||
# Google Docs
|
||||
/docs\.google\.com/[a-z]+/(?:ccc\?key=\w+|(?:u/\d+|d/(?:e/|)[0-9a-zA-Z_-]+/)?(?:edit\?[-\w=#.]*|/\?[\w=&]*|))
|
||||
# Google Drive
|
||||
\bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]*
|
||||
# Google Groups
|
||||
\bgroups\.google\.com/(?:(?:forum/#!|d/)(?:msg|topics?|searchin)|a)/[^/\s"]+/[-a-zA-Z0-9$]+(?:/[-a-zA-Z0-9]+)*
|
||||
# Google Maps
|
||||
\bmaps\.google\.com/maps\?[\w&;=]*
|
||||
# Google themes
|
||||
themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
|
||||
# Google CDN
|
||||
\bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]*
|
||||
# Goo.gl
|
||||
/goo\.gl/[a-zA-Z0-9]+
|
||||
# Google Chrome Store
|
||||
\bchrome\.google\.com/webstore/detail/[-\w]*(?:/\w*|)
|
||||
# Google Books
|
||||
\bgoogle\.(?:\w{2,4})/books(?:/\w+)*\?[-\w\d=&#.]*
|
||||
# Google Fonts
|
||||
\bfonts\.(?:googleapis|gstatic)\.com/[-/?=:;+&0-9a-zA-Z]*
|
||||
# Google Forms
|
||||
\bforms\.gle/\w+
|
||||
# Google Scholar
|
||||
\bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+
|
||||
# Google Colab Research Drive
|
||||
\bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]*
|
||||
|
||||
# GitHub SHAs (api)
|
||||
\bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b
|
||||
# GitHub SHAs (markdown)
|
||||
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
|
||||
# GitHub SHAs
|
||||
\bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b
|
||||
# GitHub wiki
|
||||
\bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b
|
||||
# githubusercontent
|
||||
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
# githubassets
|
||||
\bgithubassets.com/[0-9a-f]+(?:[-/\w.]+)
|
||||
# gist github
|
||||
\bgist\.github\.com/[^/\s"]+/[0-9a-f]+
|
||||
# git.io
|
||||
\bgit\.io/[0-9a-zA-Z]+
|
||||
# GitHub JSON
|
||||
"node_id": "[-a-zA-Z=;:/0-9+]*"
|
||||
# Contributor
|
||||
\[[^\]]+\]\(https://github\.com/[^/\s"]+\)
|
||||
# GHSA
|
||||
GHSA(?:-[0-9a-z]{4}){3}
|
||||
|
||||
# GitLab commit
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
|
||||
# GitLab merge requests
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/-/merge_requests/\d+/diffs#[0-9a-f]{40}\b
|
||||
# GitLab uploads
|
||||
\bgitlab\.[^/\s"]*/uploads/[-a-zA-Z=;:/0-9+]*
|
||||
# GitLab commits
|
||||
\bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b
|
||||
|
||||
# binanace
|
||||
accounts.binance.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
|
||||
|
||||
# bitbucket diff
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+
|
||||
# bitbucket repositories commits
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
# bitbucket commits
|
||||
\bbitbucket\.org/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
|
||||
# bit.ly
|
||||
\bbit\.ly/\w+
|
||||
|
||||
# bitrise
|
||||
\bapp\.bitrise\.io/app/[0-9a-f]*/[\w.?=&]*
|
||||
|
||||
# bootstrapcdn.com
|
||||
\bbootstrapcdn\.com/[-./\w]+
|
||||
|
||||
# cdn.cloudflare.com
|
||||
\bcdnjs\.cloudflare\.com/[./\w]+
|
||||
|
||||
# circleci
|
||||
\bcircleci\.com/gh(?:/[^/\s"]+){1,5}.[a-z]+\?[-0-9a-zA-Z=&]+
|
||||
|
||||
# gitter
|
||||
\bgitter\.im(?:/[^/\s"]+){2}\?at=[0-9a-f]+
|
||||
|
||||
# gravatar
|
||||
\bgravatar\.com/avatar/[0-9a-f]+
|
||||
|
||||
# ibm
|
||||
[a-z.]*ibm\.com/[-_#=:%!?~.\\/\d\w]*
|
||||
|
||||
# imgur
|
||||
\bimgur\.com/[^.]+
|
||||
|
||||
# Internet Archive
|
||||
\barchive\.org/web/\d+/(?:[-\w.?,'/\\+&%$#_:]*)
|
||||
|
||||
# discord
|
||||
/discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,}
|
||||
|
||||
# Disqus
|
||||
\bdisqus\.com/[-\w/%.()!?&=_]*
|
||||
|
||||
# medium link
|
||||
\blink\.medium\.com/[a-zA-Z0-9]+
|
||||
# medium
|
||||
\bmedium\.com/\@?[^/\s"]+/[-\w]+
|
||||
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
|
||||
# powerbi
|
||||
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
|
||||
# vs devops
|
||||
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
|
||||
# microsoft store
|
||||
\bmicrosoft\.com/store/apps/\w+
|
||||
|
||||
# mvnrepository.com
|
||||
\bmvnrepository\.com/[-0-9a-z./]+
|
||||
|
||||
# now.sh
|
||||
/[0-9a-z-.]+\.now\.sh\b
|
||||
|
||||
# oracle
|
||||
\bdocs\.oracle\.com/[-0-9a-zA-Z./_?#&=]*
|
||||
|
||||
# chromatic.com
|
||||
/\S+.chromatic.com\S*[")]
|
||||
|
||||
# codacy
|
||||
\bapi\.codacy\.com/project/badge/Grade/[0-9a-f]+
|
||||
|
||||
# compai
|
||||
\bcompai\.pub/v1/png/[0-9a-f]+
|
||||
|
||||
# mailgun api
|
||||
\.api\.mailgun\.net/v3/domains/[0-9a-z]+\.mailgun.org/messages/[0-9a-zA-Z=@]*
|
||||
# mailgun
|
||||
\b[0-9a-z]+.mailgun.org
|
||||
|
||||
# /message-id/
|
||||
/message-id/[-\w@./%]+
|
||||
|
||||
# Reddit
|
||||
\breddit\.com/r/[/\w_]*
|
||||
|
||||
# requestb.in
|
||||
\brequestb\.in/[0-9a-z]+
|
||||
|
||||
# sched
|
||||
\b[a-z0-9]+\.sched\.com\b
|
||||
|
||||
# Slack url
|
||||
slack://[a-zA-Z0-9?&=]+
|
||||
# Slack
|
||||
\bslack\.com/[-0-9a-zA-Z/_~?&=.]*
|
||||
# Slack edge
|
||||
\bslack-edge\.com/[-a-zA-Z0-9?&=%./]+
|
||||
# Slack images
|
||||
\bslack-imgs\.com/[-a-zA-Z0-9?&=%.]+
|
||||
|
||||
# shields.io
|
||||
\bshields\.io/[-\w/%?=&.:+;,]*
|
||||
|
||||
# stackexchange -- https://stackexchange.com/feeds/sites
|
||||
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
|
||||
|
||||
# Sentry
|
||||
[0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b
|
||||
|
||||
# Twitter markdown
|
||||
\[\@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\)
|
||||
# Twitter hashtag
|
||||
\btwitter\.com/hashtag/[\w?_=&]*
|
||||
# Twitter status
|
||||
\btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)
|
||||
# Twitter profile images
|
||||
\btwimg\.com/profile_images/[_\w./]*
|
||||
# Twitter media
|
||||
\btwimg\.com/media/[-_\w./?=]*
|
||||
# Twitter link shortened
|
||||
\bt\.co/\w+
|
||||
|
||||
# facebook
|
||||
\bfburl\.com/[0-9a-z_]+
|
||||
# facebook CDN
|
||||
\bfbcdn\.net/[\w/.,]*
|
||||
# facebook watch
|
||||
\bfb\.watch/[0-9A-Za-z]+
|
||||
|
||||
# dropbox
|
||||
\bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+
|
||||
|
||||
# ipfs protocol
|
||||
ipfs://[0-9a-z]*
|
||||
# ipfs url
|
||||
/ipfs/[0-9a-z]*
|
||||
|
||||
# w3
|
||||
\bw3\.org/[-0-9a-zA-Z/#.]+
|
||||
|
||||
# loom
|
||||
\bloom\.com/embed/[0-9a-f]+
|
||||
|
||||
# regex101
|
||||
\bregex101\.com/r/[^/\s"]+/\d+
|
||||
|
||||
# figma
|
||||
\bfigma\.com/file(?:/[0-9a-zA-Z]+/)+
|
||||
|
||||
# freecodecamp.org
|
||||
\bfreecodecamp\.org/[-\w/.]+
|
||||
|
||||
# image.tmdb.org
|
||||
\bimage\.tmdb\.org/[/\w.]+
|
||||
|
||||
# mermaid
|
||||
\bmermaid\.ink/img/[-\w]+|\bmermaid-js\.github\.io/mermaid-live-editor/#/edit/[-\w]+
|
||||
|
||||
# Wikipedia
|
||||
\ben\.wikipedia\.org/wiki/[-\w%.#]+
|
||||
|
||||
# gitweb
|
||||
[^"\s]+/gitweb/\S+;h=[0-9a-f]+
|
||||
|
||||
# HyperKitty lists
|
||||
/archives/list/[^@/]+\@[^/\s"]*/message/[^/\s"]*/
|
||||
|
||||
# lists
|
||||
/thread\.html/[^"\s]+
|
||||
|
||||
# list-management
|
||||
\blist-manage\.com/subscribe(?:[?&](?:u|id)=[0-9a-f]+)+
|
||||
|
||||
# kubectl.kubernetes.io/last-applied-configuration
|
||||
"kubectl.kubernetes.io/last-applied-configuration": ".*"
|
||||
|
||||
# pgp
|
||||
\bgnupg\.net/pks/lookup[?&=0-9a-zA-Z]*
|
||||
|
||||
# Spotify
|
||||
\bopen\.spotify\.com/embed/playlist/\w+
|
||||
|
||||
# Mastodon
|
||||
\bmastodon\.[-a-z.]*/(?:media/|\@)[?&=0-9a-zA-Z_]*
|
||||
|
||||
# scastie
|
||||
\bscastie\.scala-lang\.org/[^/]+/\w+
|
||||
|
||||
# images.unsplash.com
|
||||
\bimages\.unsplash\.com/(?:(?:flagged|reserve)/|)[-\w./%?=%&.;]+
|
||||
|
||||
# pastebin
|
||||
\bpastebin\.com/[\w/]+
|
||||
|
||||
# heroku
|
||||
\b\w+\.heroku\.com/source/archive/\w+
|
||||
|
||||
# quip
|
||||
\b\w+\.quip\.com/\w+(?:(?:#|/issues/)\w+)?
|
||||
|
||||
# badgen.net
|
||||
\bbadgen\.net/badge/[^")\]'\s]+
|
||||
|
||||
# statuspage.io
|
||||
\w+\.statuspage\.io\b
|
||||
|
||||
# media.giphy.com
|
||||
\bmedia\.giphy\.com/media/[^/]+/[\w.?&=]+
|
||||
|
||||
# tinyurl
|
||||
\btinyurl\.com/\w+
|
||||
|
||||
# getopts
|
||||
\bgetopts\s+(?:"[^"]+"|'[^']+')
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
|
||||
|
||||
# URL escaped characters
|
||||
\%[0-9A-F][A-F]
|
||||
# IPv6
|
||||
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
|
||||
# c99 hex digits (not the full format, just one I've seen)
|
||||
0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP]
|
||||
# Punycode
|
||||
\bxn--[-0-9a-z]+
|
||||
# sha
|
||||
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
|
||||
# sha-... -- uses a fancy capture
|
||||
(['"]|")[0-9a-f]{40,}\g{-1}
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
# hex in url queries
|
||||
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
|
||||
# ssh
|
||||
(?:ssh-\S+|-nistp256) [-a-zA-Z=;:/0-9+]{12,}
|
||||
|
||||
# PGP
|
||||
\b(?:[0-9A-F]{4} ){9}[0-9A-F]{4}\b
|
||||
# GPG keys
|
||||
\b(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}\b
|
||||
# Well known gpg keys
|
||||
.well-known/openpgpkey/[\w./]+
|
||||
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
# integrity
|
||||
integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}"
|
||||
|
||||
# https://www.gnu.org/software/groff/manual/groff.html
|
||||
# man troff content
|
||||
\\f[BCIPR]
|
||||
# '
|
||||
\\\(aq
|
||||
|
||||
# .desktop mime types
|
||||
^MimeTypes?=.*$
|
||||
# .desktop localized entries
|
||||
^[A-Z][a-z]+\[[a-z]+\]=.*$
|
||||
# Localized .desktop content
|
||||
Name\[[^\]]+\]=.*
|
||||
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# crypt
|
||||
"\$2[ayb]\$.{56}"
|
||||
|
||||
# scrypt / argon
|
||||
\$(?:scrypt|argon\d+[di]*)\$\S+
|
||||
|
||||
# Input to GitHub JSON
|
||||
content: "[-a-zA-Z=;:/0-9+]*="
|
||||
|
||||
# Python stringprefix / binaryprefix
|
||||
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
|
||||
(?<!')\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
|
||||
|
||||
# Regular expressions for (P|p)assword
|
||||
\([A-Z]\|[a-z]\)[a-z]+
|
||||
|
||||
# JavaScript regular expressions
|
||||
# javascript test regex
|
||||
/.*/[gim]*\.test\(
|
||||
# javascript match regex
|
||||
\.match\(/[^/\s"]*/[gim]*\s*
|
||||
# javascript match regex
|
||||
\.match\(/\\[b].*?/[gim]*\s*\)(?:;|$)
|
||||
# javascript regex
|
||||
^\s*/\\[b].*/[gim]*\s*(?:\)(?:;|$)|,$)
|
||||
# javascript replace regex
|
||||
\.replace\(/[^/\s"]*/[gim]*\s*,
|
||||
|
||||
# Go regular expressions
|
||||
regexp?\.MustCompile\(`[^`]*`\)
|
||||
|
||||
# sed regular expressions
|
||||
sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2}
|
||||
|
||||
# go install
|
||||
go install(?:\s+[a-z]+\.[-@\w/.]+)+
|
||||
|
||||
# kubernetes pod status lists
|
||||
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
|
||||
|
||||
# kubectl - pods in CrashLoopBackOff
|
||||
\w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+
|
||||
|
||||
# kubernetes object suffix
|
||||
-[0-9a-f]{10}-\w{5}\s
|
||||
|
||||
# posthog secrets
|
||||
posthog\.init\((['"])phc_[^"',]+\g{-1},
|
||||
|
||||
# xcode
|
||||
|
||||
# xcodeproject scenes
|
||||
(?:Controller|ID|id)="\w{3}-\w{2}-\w{3}"
|
||||
|
||||
# xcode api botches
|
||||
customObjectInstantitationMethod
|
||||
|
||||
# font awesome classes
|
||||
\.fa-[-a-z0-9]+
|
||||
|
||||
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
|
||||
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
|
||||
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
|
||||
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
|
||||
## You could manually change `(?i)X...` to use `[Xx]...`
|
||||
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
|
||||
# Lorem
|
||||
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
|
||||
|
||||
# Non-English
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
|
||||
|
||||
# French
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# latex
|
||||
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
|
||||
|
||||
# the negative lookahead here is to allow catching 'templatesz' as a misspelling
|
||||
# but to otherwise recognize a Windows path with \templates\foo.template or similar:
|
||||
\\(?:necessary|r(?:eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z])
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
# Note that the next example is no longer necessary if you are using
|
||||
# to match a string starting with a `#`, use a character-class:
|
||||
[#]backwards
|
||||
# version suffix <word>v#
|
||||
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
|
||||
# Compiler flags (Scala)
|
||||
(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
# Compiler flags
|
||||
#(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# Compiler flags (linker)
|
||||
,-B
|
||||
# curl arguments
|
||||
\b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
|
||||
# set arguments
|
||||
\bset(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
|
||||
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
|
||||
# macOS temp folders
|
||||
/var/folders/\w\w/[+\w]+/(?:T|-Caches-)/
|
||||
117
.github/actions/spelling/excludes.txt
vendored
Normal file
117
.github/actions/spelling/excludes.txt
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
# 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$
|
||||
(?:^|/)sources(?:|\.dep)$
|
||||
(?:^|/)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$
|
||||
\.lib$
|
||||
\.lock$
|
||||
\.map$
|
||||
\.min\..
|
||||
\.mod$
|
||||
\.mp3$
|
||||
\.mp4$
|
||||
\.o$
|
||||
\.ocf$
|
||||
\.otf$
|
||||
\.pbxproj$
|
||||
\.pdf$
|
||||
\.pem$
|
||||
\.png$
|
||||
\.psd$
|
||||
\.pyc$
|
||||
\.runsettings$
|
||||
\.s$
|
||||
\.sig$
|
||||
\.so$
|
||||
\.svg$
|
||||
\.svgz$
|
||||
\.svgz?$
|
||||
\.tar$
|
||||
\.tgz$
|
||||
\.tiff?$
|
||||
\.ttf$
|
||||
\.vsdx$
|
||||
\.wav$
|
||||
\.webm$
|
||||
\.webp$
|
||||
\.woff
|
||||
\.woff2?$
|
||||
\.xcf$
|
||||
\.xls
|
||||
\.xlsx?$
|
||||
\.xpm$
|
||||
\.yml$
|
||||
\.zip$
|
||||
^\.github/actions/spelling/
|
||||
^\.github/fabricbot.json$
|
||||
^\.gitignore$
|
||||
^\Q.git-blame-ignore-revs\E$
|
||||
^\Q.github/workflows/spelling.yml\E$
|
||||
^\Qdoc/reference/windows-terminal-logo.ans\E$
|
||||
^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$
|
||||
^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$
|
||||
^\Qsrc/host/ft_host/chafa.txt\E$
|
||||
^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$
|
||||
^\XamlStyler.json$
|
||||
^build/config/
|
||||
^consolegit2gitfilters\.json$
|
||||
^dep/
|
||||
^doc/reference/master-sequence-list.csv$
|
||||
^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$
|
||||
13
.github/actions/spelling/expect/README.md
vendored
Normal file
13
.github/actions/spelling/expect/README.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
The contents of each `.txt` file in this directory are merged together.
|
||||
|
||||
* [alphabet](alphabet.txt) is a sample for alphabet related items
|
||||
* [web](web.txt) is a sample for web/html related items
|
||||
* [expect](expect.txt) is the main list of expected items -- there is nothing
|
||||
particularly special about the file name (beyond the extension which is
|
||||
important).
|
||||
|
||||
These terms are things which temporarily exist in the project, but which
|
||||
aren't necessarily words.
|
||||
|
||||
If something is a word that could come and go, it probably belongs in a
|
||||
[dictionary](../dictionary/README.md).
|
||||
@@ -1,27 +1,33 @@
|
||||
AAAa
|
||||
AAAAA
|
||||
AAAAAAAAAAAAA
|
||||
AAAAAABBBBBBCCC
|
||||
AAAAABBBBBBCCC
|
||||
abcd
|
||||
abcd
|
||||
abcde
|
||||
abcdef
|
||||
ABCDEFG
|
||||
ABCDEFGH
|
||||
ABCDEFGHIJ
|
||||
abcdefghijk
|
||||
ABCDEFGHIJKLMNO
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
QQQQQ
|
||||
QQQQQQQQQ
|
||||
QQQQQQQQQQ
|
||||
ABCG
|
||||
ABE
|
||||
abf
|
||||
BBBBB
|
||||
BBBBBBBB
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBGGRR
|
||||
EFG
|
||||
EFGh
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
|
||||
qrstuvwxyz
|
||||
qwerty
|
||||
QWERTYUIOP
|
||||
qwertyuiopasdfg
|
||||
TTTTTTTTTTTTTTTTTTTTTTTTTT
|
||||
VVVVVVVVVVVVVVVV
|
||||
yyyy
|
||||
YYYYYYYDDDDDDDDDDD
|
||||
ZAAZZ
|
||||
ZABBZ
|
||||
ZBAZZ
|
||||
File diff suppressed because it is too large
Load Diff
6
.github/actions/spelling/expect/web.txt
vendored
Normal file
6
.github/actions/spelling/expect/web.txt
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
WCAG
|
||||
winui
|
||||
appshellintegration
|
||||
mdtauk
|
||||
gfycat
|
||||
Guake
|
||||
62
.github/actions/spelling/line_forbidden.patterns
vendored
Normal file
62
.github/actions/spelling/line_forbidden.patterns
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
# reject `m_data` as there's a certain OS which has evil defines that break things if it's used elsewhere
|
||||
# \bm_data\b
|
||||
|
||||
# If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test,
|
||||
# you might not want to check in code where you were debugging w/ `fit()`, in which case, you might want
|
||||
# to use this:
|
||||
#\bfit\(
|
||||
|
||||
# s.b. GitHub
|
||||
\bGithub\b
|
||||
|
||||
# s.b. GitLab
|
||||
\bGitlab\b
|
||||
|
||||
# s.b. JavaScript
|
||||
\bJavascript\b
|
||||
|
||||
# s.b. Microsoft
|
||||
\bMicroSoft\b
|
||||
|
||||
# s.b. another
|
||||
\ban[- ]other\b
|
||||
|
||||
# s.b. greater than
|
||||
\bgreater then\b
|
||||
|
||||
# s.b. into
|
||||
#\sin to\s
|
||||
|
||||
# s.b. opt-in
|
||||
\sopt in\s
|
||||
|
||||
# s.b. less than
|
||||
\bless then\b
|
||||
|
||||
# s.b. otherwise
|
||||
\bother[- ]wise\b
|
||||
|
||||
# s.b. nonexistent
|
||||
\bnon existing\b
|
||||
\b[Nn]o[nt][- ]existent\b
|
||||
|
||||
# s.b. preexisting
|
||||
[Pp]re[- ]existing
|
||||
|
||||
# s.b. preempt
|
||||
[Pp]re[- ]empt\b
|
||||
|
||||
# s.b. preemptively
|
||||
[Pp]re[- ]emptively
|
||||
|
||||
# s.b. reentrancy
|
||||
[Rr]e[- ]entrancy
|
||||
|
||||
# s.b. reentrant
|
||||
[Rr]e[- ]entrant
|
||||
|
||||
# s.b. workaround(s)
|
||||
#\bwork[- ]arounds?\b
|
||||
|
||||
# Reject duplicate words
|
||||
\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s
|
||||
96
.github/actions/spelling/patterns/patterns.txt
vendored
Normal file
96
.github/actions/spelling/patterns/patterns.txt
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
# 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
|
||||
microsoft/cascadia-code\@[0-9a-fA-F]{40}
|
||||
\d+x\d+Logo
|
||||
Scro\&ll
|
||||
# selectionInput.cpp
|
||||
:\\windows\\syste\b
|
||||
TestUtils::VerifyExpectedString\(tb, L"[^"]+"
|
||||
(?:hostSm|mach)\.ProcessString\(L"[^"]+"
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
0x[0-9A-Za-z]+
|
||||
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
12
.github/actions/spelling/reject.txt
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
^attache$
|
||||
^attacher$
|
||||
^attachers$
|
||||
benefitting
|
||||
occurences?
|
||||
^dependan.*
|
||||
^oer$
|
||||
Sorce
|
||||
^[Ss]pae.*
|
||||
^untill$
|
||||
^untilling$
|
||||
^wether.*
|
||||
20
.github/workflows/spelling.yml
vendored
20
.github/workflows/spelling.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Spell checking
|
||||
on:
|
||||
push:
|
||||
schedule:
|
||||
# * is a special character in YAML so you have to quote this string
|
||||
- cron: '15 * * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Spell checking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.0.0
|
||||
with:
|
||||
fetch-depth: 5
|
||||
- uses: check-spelling/check-spelling@0.0.12-alpha
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
bucket: .github/actions
|
||||
project: spell-check
|
||||
134
.github/workflows/spelling2.yml
vendored
Normal file
134
.github/workflows/spelling2.yml
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
# spelling.yml is blocked per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-g86g-chm8-7r2p
|
||||
name: Spell checking
|
||||
|
||||
# Comment management is handled through a secondary job, for details see:
|
||||
# https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions
|
||||
#
|
||||
# `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# (in odd cases, it might actually run just to collapse a commment, but that's fairly rare)
|
||||
# it needs `contents: write` in order to add a comment.
|
||||
#
|
||||
# `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# or collapse a comment (in the case where it had previously made a comment and now no longer needs to show a comment)
|
||||
# it needs `pull-requests: write` in order to manipulate those comments.
|
||||
|
||||
# Updating pull request branches is managed via comment handling.
|
||||
# For details, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-expect-list
|
||||
#
|
||||
# These elements work together to make it happen:
|
||||
#
|
||||
# `on.issue_comment`
|
||||
# This event listens to comments by users asking to update the metadata.
|
||||
#
|
||||
# `jobs.update`
|
||||
# This job runs in response to an issue_comment and will push a new commit
|
||||
# to update the spelling metadata.
|
||||
#
|
||||
# `with.experimental_apply_changes_via_bot`
|
||||
# Tells the action to support and generate messages that enable it
|
||||
# to make a commit to update the spelling metadata.
|
||||
#
|
||||
# `with.ssh_key`
|
||||
# In order to trigger workflows when the commit is made, you can provide a
|
||||
# secret (typically, a write-enabled github deploy key).
|
||||
#
|
||||
# For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**"
|
||||
types:
|
||||
- 'opened'
|
||||
- 'reopened'
|
||||
- 'synchronize'
|
||||
issue_comment:
|
||||
types:
|
||||
- 'created'
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
name: Spell checking
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
actions: read
|
||||
outputs:
|
||||
followup: ${{ steps.spelling.outputs.followup }}
|
||||
runs-on: ubuntu-latest
|
||||
if: "contains(github.event_name, 'pull_request') || github.event_name == 'push'"
|
||||
concurrency:
|
||||
group: spelling-${{ github.event.pull_request.number || github.ref }}
|
||||
# note: If you use only_check_changed_files, you do not want cancel-in-progress
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
suppress_push_for_open_pull_request: 1
|
||||
checkout: true
|
||||
check_file_names: 1
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
post_comment: 0
|
||||
use_magic_file: 1
|
||||
extra_dictionary_limit: 10
|
||||
extra_dictionaries:
|
||||
cspell:software-terms/src/software-terms.txt
|
||||
cspell:python/src/python/python-lib.txt
|
||||
cspell:node/node.txt
|
||||
cspell:cpp/src/stdlib-c.txt
|
||||
cspell:cpp/src/stdlib-cpp.txt
|
||||
cspell:fullstack/fullstack.txt
|
||||
cspell:filetypes/filetypes.txt
|
||||
cspell:html/html.txt
|
||||
cspell:cpp/src/compiler-msvc.txt
|
||||
cspell:python/src/common/extra.txt
|
||||
cspell:powershell/powershell.txt
|
||||
cspell:aws/aws.txt
|
||||
cspell:cpp/src/lang-keywords.txt
|
||||
cspell:npm/npm.txt
|
||||
cspell:dotnet/dotnet.txt
|
||||
cspell:python/src/python/python.txt
|
||||
cspell:css/css.txt
|
||||
cspell:cpp/src/stdlib-cmath.txt
|
||||
check_extra_dictionaries: ''
|
||||
|
||||
comment-push:
|
||||
name: Report (Push)
|
||||
# If your workflow isn't running on push, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
needs: spelling
|
||||
permissions:
|
||||
contents: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
|
||||
comment-pr:
|
||||
name: Report (PR)
|
||||
# If you workflow isn't running on pull_request*, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
needs: spelling
|
||||
permissions:
|
||||
pull-requests: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
@@ -150,7 +150,6 @@ Please file new issues, feature requests and suggestions, but **DO search for si
|
||||
If you would like to ask a question that you feel doesn't warrant an issue (yet), please reach out to us via Twitter:
|
||||
|
||||
* Kayla Cinnamon, Program Manager: [@cinnamon\_msft](https://twitter.com/cinnamon_msft)
|
||||
* Rich Turner, Program Manager: [@richturn\_ms](https://twitter.com/richturn_ms)
|
||||
* Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett)
|
||||
* Michael Niksa, Senior Developer: [@michaelniksa](https://twitter.com/MichaelNiksa)
|
||||
* Mike Griese, Developer: [@zadjii](https://twitter.com/zadjii)
|
||||
|
||||
@@ -27,21 +27,43 @@ variables:
|
||||
# 0.0.1904.0900
|
||||
name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
|
||||
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
stages:
|
||||
- stage: Audit_x64
|
||||
displayName: Audit Mode
|
||||
dependsOn: []
|
||||
condition: succeeded()
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
- stage: Build_x64
|
||||
displayName: Build x64
|
||||
dependsOn: []
|
||||
condition: succeeded()
|
||||
jobs:
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
- stage: Build_x86
|
||||
displayName: Build x86
|
||||
dependsOn: []
|
||||
condition: not(eq(variables['Build.Reason'], 'PullRequest'))
|
||||
jobs:
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
- stage: Build_ARM64
|
||||
displayName: Build ARM64
|
||||
dependsOn: []
|
||||
condition: not(eq(variables['Build.Reason'], 'PullRequest'))
|
||||
jobs:
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: ARM64
|
||||
- stage: Scripts
|
||||
displayName: Code Health Scripts
|
||||
dependsOn: []
|
||||
condition: succeeded()
|
||||
jobs:
|
||||
- template: ./templates/check-formatting.yml
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: ARM64
|
||||
|
||||
- template: ./templates/check-formatting.yml
|
||||
|
||||
@@ -7,13 +7,14 @@ Properties listed below affect the entire window, regardless of the profile sett
|
||||
| -------- | --------- | ---- | ------- | ----------- |
|
||||
| `alwaysShowTabs` | _Required_ | Boolean | `true` | When set to `true`, tabs are always displayed. When set to `false` and `showTabsInTitlebar` is set to `false`, tabs only appear after typing <kbd>Ctrl</kbd> + <kbd>T</kbd>. |
|
||||
| `copyOnSelect` | Optional | Boolean | `false` | When set to `true`, a selection is immediately copied to your clipboard upon creation. When set to `false`, the selection persists and awaits further action. |
|
||||
| `copyFormatting` | Optional | Boolean | `false` | When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. |
|
||||
| `defaultProfile` | _Required_ | String | PowerShell guid | Sets the default profile. Opens by typing <kbd>Ctrl</kbd> + <kbd>T</kbd> or by clicking the '+' icon. The guid of the desired default profile is used as the value. |
|
||||
| `initialCols` | _Required_ | Integer | `120` | The number of columns displayed in the window upon first load. |
|
||||
| `initialPosition` | Optional | String | `","` | The position of the top left corner of the window upon first load. On a system with multiple displays, these coordinates are relative to the top left of the primary display. If `launchMode` is set to `"maximized"`, the window will be maximized on the monitor specified by those coordinates. |
|
||||
| `initialRows` | _Required_ | Integer | `30` | The number of rows displayed in the window upon first load. |
|
||||
| `launchMode` | Optional | String | `default` | Defines whether the Terminal will launch as maximized or not. Possible values: `"default"`, `"maximized"` |
|
||||
| `rowsToScroll` | Optional | Integer | `system` | The number of rows to scroll at a time with the mouse wheel. This will override the system setting if the value is not zero or "system". |
|
||||
| `requestedTheme` | _Required_ | String | `system` | Sets the theme of the application. Possible values: `"light"`, `"dark"`, `"system"` |
|
||||
| `theme` | _Required_ | String | `system` | Sets the theme of the application. Possible values: `"light"`, `"dark"`, `"system"` |
|
||||
| `showTerminalTitleInTitlebar` | _Required_ | Boolean | `true` | When set to `true`, titlebar displays the title of the selected tab. When set to `false`, titlebar displays "Windows Terminal". |
|
||||
| `showTabsInTitlebar` | Optional | Boolean | `true` | When set to `true`, the tabs are moved into the titlebar and the titlebar disappears. When set to `false`, the titlebar sits above the tabs. |
|
||||
| `snapToGridOnResize` | Optional | Boolean | `false` | When set to `true`, the window will snap to the nearest character boundary on resize. When `false`, the window will resize "smoothly" |
|
||||
@@ -37,7 +38,6 @@ Properties listed below are specific to each unique profile.
|
||||
| `backgroundImageStretchMode` | Optional | String | `uniformToFill` | Sets how the background image is resized to fill the window. Possible values: `"none"`, `"fill"`, `"uniform"`, `"uniformToFill"` |
|
||||
| `closeOnExit` | Optional | String | `graceful` | Sets how the profile reacts to termination or failure to launch. Possible values: `"graceful"` (close when `exit` is typed or the process exits normally), `"always"` (always close) and `"never"` (never close). `true` and `false` are accepted as synonyms for `"graceful"` and `"never"` respectively. |
|
||||
| `colorScheme` | Optional | String | `Campbell` | Name of the terminal color scheme to use. Color schemes are defined under `schemes`. |
|
||||
| `colorTable` | Optional | Array[String] | | Array of colors used in the profile if `colorscheme` is not set. Array follows the format defined in `schemes`. |
|
||||
| `commandline` | Optional | String | | Executable used in the profile. |
|
||||
| `cursorColor` | Optional | String | | Sets the cursor color of the profile. Overrides `cursorColor` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `cursorHeight` | Optional | Integer | | Sets the percentage height of the cursor starting from the bottom. Only works when `cursorShape` is set to `"vintage"`. Accepts values from 25-100. |
|
||||
@@ -49,12 +49,12 @@ Properties listed below are specific to each unique profile.
|
||||
| `historySize` | Optional | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. |
|
||||
| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. |
|
||||
| `padding` | Optional | String | `8, 8, 8, 8` | Sets the padding around the text within the window. Can have three different formats: `"#"` sets the same padding for all sides, `"#, #"` sets the same padding for left-right and top-bottom, and `"#, #, #, #"` sets the padding individually for left, top, right, and bottom. |
|
||||
| `scrollbarState` | Optional | String | | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` |
|
||||
| `scrollbarState` | Optional | String | `"visible"` | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` |
|
||||
| `selectionBackground` | Optional | String | | Sets the selection background color of the profile. Overrides `selectionBackground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `snapOnInput` | Optional | Boolean | `true` | When set to `true`, the window will scroll to the command input line when typing. When set to `false`, the window will not scroll when you start typing. |
|
||||
| `source` | Optional | String | | Stores the name of the profile generator that originated this profile. _There are no discoverable values for this field._ |
|
||||
| `startingDirectory` | Optional | String | `%USERPROFILE%` | The directory the shell starts in when it is loaded. |
|
||||
| `suppressApplicationTitle` | Optional | Boolean | | When set to `true`, `tabTitle` overrides the default title of the tab and any title change messages from the application will be suppressed. When set to `false`, `tabTitle` behaves as normal. |
|
||||
| `suppressApplicationTitle` | Optional | Boolean | `false` | When set to `true`, `tabTitle` overrides the default title of the tab and any title change messages from the application will be suppressed. When set to `false`, `tabTitle` behaves as normal. |
|
||||
| `tabTitle` | Optional | String | | If set, will replace the `name` as the title to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. |
|
||||
| `useAcrylic` | Optional | Boolean | `false` | When set to `true`, the window will have an acrylic background. When set to `false`, the window will have a plain, untextured background. The transparency only applies to focused windows due to OS limitation. |
|
||||
| `experimental.retroTerminalEffect` | Optional | Boolean | `false` | When set to `true`, enable retro terminal effects. This is an experimental feature, and its continued existence is not guaranteed. |
|
||||
@@ -111,14 +111,13 @@ For commands with arguments:
|
||||
|
||||
| Command | Command Description | Action (*=required) | Action Arguments | Argument Descriptions |
|
||||
| ------- | ------------------- | ------ | ---------------- | ----------------- |
|
||||
| `adjustFontSize` | Change the text size by a specified point amount. | `delta` | integer | Amount of size change per command invocation. |
|
||||
| `closePane` | Close the active pane. | | | |
|
||||
| `closeTab` | Close the current tab. | | | |
|
||||
| `closeWindow` | Close the current window and all tabs within it. | | | |
|
||||
| `copy` | Copy the selected terminal content to your Windows Clipboard. | `trimWhitespace` | boolean | When `true`, newlines persist from the selected text. When `false`, copied content will paste on one line. |
|
||||
| `decreaseFontSize` | Make the text smaller by one delta. | `delta` | integer | Amount of size decrease per command invocation. |
|
||||
| `copy` | Copy the selected terminal content to your Windows Clipboard. | `singleLine` | boolean | When `true`, the copied content will be copied as a single line. When `false`, newlines persist from the selected text. |
|
||||
| `duplicateTab` | Make a copy and open the current tab. | | | |
|
||||
| `find` | Open the search dialog box. | | | |
|
||||
| `increaseFontSize` | Make the text larger by one delta. | `delta` | integer | Amount of size increase per command invocation. |
|
||||
| `moveFocus` | Focus on a different pane depending on direction. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the focus will move. |
|
||||
| `newTab` | Create a new tab. Without any arguments, this will open the default profile in a new tab. | 1. `commandLine`<br>2. `startingDirectory`<br>3. `tabTitle`<br>4. `index`<br>5. `profile` | 1. string<br>2. string<br>3. string<br>4. integer<br>5. string | 1. Executable run within the tab.<br>2. Directory in which the tab will open.<br>3. Title of the new tab.<br>4. Profile that will open based on its position in the dropdown (starting at 0).<br>5. Profile that will open based on its GUID or name. |
|
||||
| `nextTab` | Open the tab to the right of the current one. | | | |
|
||||
@@ -132,7 +131,7 @@ For commands with arguments:
|
||||
| `scrollUp` | Move the screen up. | | | |
|
||||
| `scrollUpPage` | Move the screen up a whole page. | | | |
|
||||
| `scrollDownPage` | Move the screen down a whole page. | | | |
|
||||
| `splitPane` | Halve the size of the active pane and open another. Without any arguments, this will open the default profile in the new pane. | 1. `split`*<br>2. `commandLine`<br>3. `startingDirectory`<br>4. `tabTitle`<br>5. `index`<br>6. `profile` | 1. `vertical`, `horizontal`, `auto`<br>2. string<br>3. string<br>4. string<br>5. integer<br>6. string | 1. How the pane will split. `auto` will split in the direction that provides the most surface area.<br>2. Executable run within the pane.<br>3. Directory in which the pane will open.<br>4. Title of the tab when the new pane is focused.<br>5. Profile that will open based on its position in the dropdown (starting at 0).<br>6. Profile that will open based on its GUID or name. |
|
||||
| `splitPane` | Halve the size of the active pane and open another. Without any arguments, this will open the default profile in the new pane. | 1. `split`*<br>2. `commandLine`<br>3. `startingDirectory`<br>4. `tabTitle`<br>5. `index`<br>6. `profile`<br>7. `splitMode` | 1. `vertical`, `horizontal`, `auto`<br>2. string<br>3. string<br>4. string<br>5. integer<br>6. string<br>7. string | 1. How the pane will split. `auto` will split in the direction that provides the most surface area.<br>2. Executable run within the pane.<br>3. Directory in which the pane will open.<br>4. Title of the tab when the new pane is focused.<br>5. Profile that will open based on its position in the dropdown (starting at 0).<br>6. Profile that will open based on its GUID or name.<br>7. Controls how the pane splits. Only accepts `duplicate` which will duplicate the focused pane's profile into a new pane. |
|
||||
| `switchToTab` | Open a specific tab depending on index. | `index`* | integer | Tab that will open based on its position in the tab bar (starting at 0). |
|
||||
| `toggleFullscreen` | Switch between fullscreen and default window sizes. | | | |
|
||||
| `unbound` | Unbind the associated keys from any command. | | | |
|
||||
|
||||
@@ -25,29 +25,15 @@
|
||||
},
|
||||
"ShortcutActionName": {
|
||||
"enum": [
|
||||
"adjustFontSize",
|
||||
"closePane",
|
||||
"closeTab",
|
||||
"closeWindow",
|
||||
"copy",
|
||||
"copyTextWithoutNewlines",
|
||||
"decreaseFontSize",
|
||||
"duplicateTab",
|
||||
"increaseFontSize",
|
||||
"moveFocus",
|
||||
"moveFocusDown",
|
||||
"moveFocusLeft",
|
||||
"moveFocusRight",
|
||||
"moveFocusUp",
|
||||
"newTab",
|
||||
"newTabProfile0",
|
||||
"newTabProfile1",
|
||||
"newTabProfile2",
|
||||
"newTabProfile3",
|
||||
"newTabProfile4",
|
||||
"newTabProfile5",
|
||||
"newTabProfile6",
|
||||
"newTabProfile7",
|
||||
"newTabProfile8",
|
||||
"nextTab",
|
||||
"openNewTabDropdown",
|
||||
"openSettings",
|
||||
@@ -55,29 +41,15 @@
|
||||
"prevTab",
|
||||
"resetFontSize",
|
||||
"resizePane",
|
||||
"resizePaneDown",
|
||||
"resizePaneLeft",
|
||||
"resizePaneRight",
|
||||
"resizePaneUp",
|
||||
"scrollDown",
|
||||
"scrollDownPage",
|
||||
"scrollUp",
|
||||
"scrollUpPage",
|
||||
"splitHorizontal",
|
||||
"splitVertical",
|
||||
"splitPane",
|
||||
"switchToTab",
|
||||
"switchToTab0",
|
||||
"switchToTab1",
|
||||
"switchToTab2",
|
||||
"switchToTab3",
|
||||
"switchToTab4",
|
||||
"switchToTab5",
|
||||
"switchToTab6",
|
||||
"switchToTab7",
|
||||
"switchToTab8",
|
||||
"toggleFullscreen",
|
||||
"find"
|
||||
"find",
|
||||
"unbound"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -135,6 +107,23 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AdjustFontSizeAction": {
|
||||
"description": "Arguments corresponding to an Adjust Font Size Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "adjustFontSize" },
|
||||
"delta": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "How much to change the current font point size"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "delta" ]
|
||||
},
|
||||
"CopyAction": {
|
||||
"description": "Arguments corresponding to a Copy Text Action",
|
||||
"allOf": [
|
||||
@@ -142,10 +131,10 @@
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "copy" },
|
||||
"trimWhitespace": {
|
||||
"singleLine": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "If true, whitespace is removed and newlines are maintained. If false, newlines are removed and whitespace is maintained."
|
||||
"default": false,
|
||||
"description": "If true, newlines are removed and whitespace is maintained. If false, whitespace is removed and newlines are maintained."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,11 +215,14 @@
|
||||
"$ref": "#/definitions/SplitState",
|
||||
"default": "auto",
|
||||
"description": "The orientation to split the pane in, either vertical (think [|]), horizontal (think [-]), or auto (splits pane based on remaining space)"
|
||||
},
|
||||
"splitMode": {
|
||||
"default": "duplicate",
|
||||
"description": "Control how the pane splits. Only accepts `duplicate` which will duplicate the focused pane's profile into a new pane."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "split" ]
|
||||
]
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
@@ -238,13 +230,15 @@
|
||||
"command": {
|
||||
"description": "The action executed when the associated key bindings are pressed.",
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/AdjustFontSizeAction" },
|
||||
{ "$ref": "#/definitions/CopyAction" },
|
||||
{ "$ref": "#/definitions/ShortcutActionName" },
|
||||
{ "$ref": "#/definitions/NewTabAction" },
|
||||
{ "$ref": "#/definitions/SwitchToTabAction" },
|
||||
{ "$ref": "#/definitions/MoveFocusAction" },
|
||||
{ "$ref": "#/definitions/ResizePaneAction" },
|
||||
{ "$ref": "#/definitions/SplitPaneAction" }
|
||||
{ "$ref": "#/definitions/SplitPaneAction" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
"keys": {
|
||||
@@ -283,6 +277,11 @@
|
||||
"description": "When set to true, a selection is immediately copied to your clipboard upon creation. When set to false, the selection persists and awaits further action.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyFormatting": {
|
||||
"default": false,
|
||||
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"defaultProfile": {
|
||||
"$ref": "#/definitions/ProfileGuid",
|
||||
"description": "Sets the default profile. Opens by clicking the '+' icon or typing the key binding assigned to 'newTab'. The guid of the desired default profile is used as the value."
|
||||
@@ -328,7 +327,7 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"requestedTheme": {
|
||||
"theme": {
|
||||
"default": "system",
|
||||
"description": "Sets the theme of the application.",
|
||||
"enum": [
|
||||
@@ -407,7 +406,7 @@
|
||||
},
|
||||
"backgroundImage": {
|
||||
"description": "Sets the file location of the Image to draw over the window background.",
|
||||
"type": "string"
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"backgroundImageAlignment": {
|
||||
"default": "center",
|
||||
@@ -561,7 +560,7 @@
|
||||
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 25-100.",
|
||||
"maximum": 100,
|
||||
"minimum": 25,
|
||||
"type": "integer"
|
||||
"type": ["integer","null"]
|
||||
},
|
||||
"cursorShape": {
|
||||
"default": "bar",
|
||||
@@ -613,7 +612,7 @@
|
||||
},
|
||||
"icon": {
|
||||
"description": "Image file location of the icon used in the profile. Displays within the tab and the dropdown menu.",
|
||||
"type": "string"
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of the profile. Displays in the dropdown menu.",
|
||||
@@ -646,7 +645,7 @@
|
||||
},
|
||||
"source": {
|
||||
"description": "Stores the name of the profile generator that originated this profile.",
|
||||
"type": "string"
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"startingDirectory": {
|
||||
"description": "The directory the shell starts in when it is loaded.",
|
||||
@@ -658,7 +657,7 @@
|
||||
},
|
||||
"tabTitle": {
|
||||
"description": "If set, will replace the name as the title to pass to the shell on startup. Some shells (like bash) may choose to ignore this initial value, while others (cmd, powershell) may use this value over the lifetime of the application.",
|
||||
"type": "string"
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"useAcrylic": {
|
||||
"default": false,
|
||||
@@ -789,37 +788,15 @@
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/Globals" },
|
||||
{
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/Globals" },
|
||||
{
|
||||
"additionalItems": true,
|
||||
"properties": {
|
||||
"profiles": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/ProfileList" },
|
||||
{ "$ref": "#/definitions/ProfilesObject" }
|
||||
]
|
||||
},
|
||||
"schemes": { "$ref": "#/definitions/SchemeList" }
|
||||
},
|
||||
"required": [
|
||||
"profiles",
|
||||
"schemes",
|
||||
"defaultProfile"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"additionalItems": false,
|
||||
"additionalItems": true,
|
||||
"properties": {
|
||||
"globals": { "$ref": "#/definitions/Globals" },
|
||||
"profiles": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/ProfileList" },
|
||||
{ "$ref": "#/definitions/ProfilesObject" }
|
||||
{ "$ref": "#/definitions/ProfileList" },
|
||||
{ "$ref": "#/definitions/ProfilesObject" }
|
||||
]
|
||||
},
|
||||
"schemes": { "$ref": "#/definitions/SchemeList" }
|
||||
@@ -827,7 +804,7 @@
|
||||
"required": [
|
||||
"profiles",
|
||||
"schemes",
|
||||
"globals"
|
||||
"defaultProfile"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
88
doc/specs/#4191 - Formatted Copy/spec.md
Normal file
88
doc/specs/#4191 - Formatted Copy/spec.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
author: Kayla Cinnamon @cinnamon-msft
|
||||
created on: 2020-04-01
|
||||
last updated: 2020-04-07
|
||||
issue id: #4191
|
||||
---
|
||||
|
||||
# Formatted Copy
|
||||
|
||||
## Abstract
|
||||
|
||||
When copying text, the Terminal should provide the option of including formatting. Not all apps that receive text allow for picking which format you want when pasting. The default should be to only copy plain text, based on the response from this poll on Twitter.
|
||||
|
||||

|
||||
|
||||
## Solution Proposals
|
||||
|
||||
A proposal for the right click behavior as well as two user settings proposals are described below. The conclusion the team arrived at is at the bottom under the [Conclusions section](#conclusions).
|
||||
|
||||
1. [Settings option 1 - global setting](#settings-option-1---global-setting)
|
||||
2. [Settings option 2 - key binding argument](#settings-option-2---key-binding-argument)
|
||||
3. [Right click behavior](#right-click-behavior)
|
||||
|
||||
### Settings option 1 - global setting
|
||||
|
||||
We could have a global setting that when enabled, would copy formatting to the clipboard on all copy operations.
|
||||
|
||||
### Settings option 2 - key binding argument
|
||||
|
||||
We could add an argument to the `copy` key binding argument to allow for formatted copying when the user chooses to do so.
|
||||
|
||||
### Right click behavior
|
||||
|
||||
By default, right clicking to copy would only copy the plain text.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
### Settings option 1 - global setting
|
||||
|
||||
a. The user could list which kinds of formats they want included when they copy. When right clicking, they would copy with these formats.
|
||||
|
||||
`"copyFormats": ["html","rtf","plain"]`
|
||||
|
||||
b. We could also just combine html and rtf into a single boolean. Users would either get plain text only (`false`) or all formatting (`true`) onto their clipboard. If this is set to `true`, the default right click behavior is reversed: right clicking copies the formatting.
|
||||
|
||||
`"copyFormatting": true`
|
||||
|
||||
### Settings option 2 - key binding argument
|
||||
|
||||
a. Just like the `trimWhitespace` argument you can add to the `copy` key binding, we could add one for text formatting. This would not change the right click behavior.
|
||||
|
||||
`{"command": {"action": "copy", "keepFormatting": true}, "keys": "ctrl+a"}`
|
||||
|
||||
b. We could also split out the html and rtf formats. The right click behavior would still stay as default.
|
||||
|
||||
`{"command": {"action": "copy", "formats": ["html","rtf","plain"]}, "keys": "ctrl+a"}`
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
This shouldn't affect accessibility.
|
||||
|
||||
### Security
|
||||
|
||||
This does not affect security.
|
||||
|
||||
### Reliability
|
||||
|
||||
This does not affect reliability.
|
||||
|
||||
### Compatibility
|
||||
|
||||
This breaks the existing behavior of always copying the formatting. The justification for breaking this default behavior is in response to the community saying the default should be plain text only.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
## Potential Issues
|
||||
|
||||
One possible issue is that discovering how to copy the formatting might be difficult to find. We could mitigate this by adding it into the settings.json file and commenting it out.
|
||||
|
||||
## Conclusions
|
||||
|
||||
The team has decided to have plain text as the default copy behavior and to enable formatted copying with a global setting that accepts a boolean value (settings option 1 - global setting, option b). In the future, we can modify this setting to also accept an array, so the user can specify which formats they would like to copy. Additionally, a key binding can be added to allow for greater flexibility.
|
||||
|
||||
## Future considerations
|
||||
|
||||
We could always add an additional option if people want more flexibility. For example, if we ship a global setting now, we could ship a key binding later that lets you choose how you want to copy, and vice versa. Additionally, we can add functionality to the global setting that allows for specific formats or styles to be copied.
|
||||
BIN
doc/specs/#4191 - Formatted Copy/twitter-poll.png
Normal file
BIN
doc/specs/#4191 - Formatted Copy/twitter-poll.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
@@ -507,7 +507,7 @@ runtimeclass TerminalParameters {
|
||||
* [ ] Add a `ShortcutAction` for `FocusPane`, which accepts a single parameter
|
||||
`index`.
|
||||
- We'll need to track each `Pane`'s ID as `Pane`s are created, so that we can
|
||||
quickly switch to the i'th `Pane`.
|
||||
quickly switch to the nth `Pane`.
|
||||
- This is in order to support the `-t,--target` parameter of `split-pane`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This doc will hopefully provide a useful guide for adding profiles for common
|
||||
third-party tools to your
|
||||
[profiles.json](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingJsonSettings.md)
|
||||
[settings.json](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingJsonSettings.md)
|
||||
file.
|
||||
|
||||
All of these profiles are provided _without_ their `guid` set. If you'd like to
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Editing Windows Terminal JSON Settings
|
||||
|
||||
One way (currently the only way) to configure Windows Terminal is by editing the
|
||||
`profiles.json` settings file. At the time of writing you can open the settings
|
||||
`settings.json` settings file. At the time of writing you can open the settings
|
||||
file in your default editor by selecting `Settings` from the WT pull down menu.
|
||||
|
||||
The settings are stored in the file `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\profiles.json`.
|
||||
The settings are stored in the file `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json`.
|
||||
|
||||
As of [#2515](https://github.com/microsoft/terminal/pull/2515), the settings are
|
||||
split into _two_ files: a hardcoded `defaults.json`, and `profiles.json`, which
|
||||
split into _two_ files: a hardcoded `defaults.json`, and `settings.json`, which
|
||||
contains the user settings. Users should only be concerned with the contents of
|
||||
the `profiles.json`, which contains their customizations. The `defaults.json`
|
||||
the `settings.json`, which contains their customizations. The `defaults.json`
|
||||
file is only provided as a reference of what the default settings are. For more
|
||||
details on how these two files work, see [Settings
|
||||
Layering](#settings-layering). To view the default settings file, click on the
|
||||
@@ -164,7 +164,7 @@ The values for background image stretch mode are documented [here](https://docs.
|
||||
### Hiding a profile
|
||||
|
||||
If you want to remove a profile from the list of profiles in the new tab
|
||||
dropdown, but keep the profile around in your `profiles.json` file, you can add
|
||||
dropdown, but keep the profile around in your `settings.json` file, you can add
|
||||
the property `"hidden": true` to the profile's json. This can also be used to
|
||||
remove the default `cmd` and PowerShell profiles, if the user does not wish to
|
||||
see them.
|
||||
@@ -198,10 +198,10 @@ The runtime settings are actually constructed from _three_ sources:
|
||||
profiles for both Windows PowerShell and Command Prompt (`cmd.exe`).
|
||||
* Dynamic Profiles, which are generated at runtime. These include Powershell
|
||||
Core, the Azure Cloud Shell connector, and profiles for and WSL distros.
|
||||
* The user settings from `profiles.json`.
|
||||
* The user settings from `settings.json`.
|
||||
|
||||
Settings from each of these sources are "layered" upon the settings from
|
||||
previous sources. In this manner, the user settings in `profiles.json` can
|
||||
previous sources. In this manner, the user settings in `settings.json` can
|
||||
contain _only the changes from the default settings_. For example, if a user
|
||||
would like to only change the color scheme of the default `cmd` profile to
|
||||
"Solarized Dark", you could change your cmd profile to the following:
|
||||
@@ -220,7 +220,7 @@ with that GUID will all be treated as the same object. Any changes in that
|
||||
profile will overwrite those from the defaults.
|
||||
|
||||
Similarly, you can overwrite settings from a color scheme by defining a color
|
||||
scheme in `profiles.json` with the same name as a default color scheme.
|
||||
scheme in `settings.json` with the same name as a default color scheme.
|
||||
|
||||
If you'd like to unbind a keystroke that's bound to an action in the default
|
||||
keybindings, you can set the `"command"` to `"unbound"` or `null`. This will
|
||||
@@ -230,9 +230,9 @@ performing the default action.
|
||||
### Dynamic Profiles
|
||||
|
||||
When dynamic profiles are created at runtime, they'll be added to the
|
||||
`profiles.json` file. You can identify these profiles by the presence of a
|
||||
`settings.json` file. You can identify these profiles by the presence of a
|
||||
`"source"` property. These profiles are tied to their source - if you uninstall
|
||||
a linux distro, then the profile will remain in your `profiles.json` file, but
|
||||
a linux distro, then the profile will remain in your `settings.json` file, but
|
||||
the profile will be hidden.
|
||||
|
||||
The Windows Terminal uses the `guid` property of these dynamically-generated
|
||||
@@ -246,7 +246,7 @@ like to hide all the WSL profiles, you could add the following setting:
|
||||
|
||||
```json
|
||||
|
||||
"disabledProfileSources": ["Microsoft.Terminal.WSL"],
|
||||
"disabledProfileSources": ["Windows.Terminal.WSL"],
|
||||
...
|
||||
|
||||
```
|
||||
@@ -371,7 +371,7 @@ In the above settings, the `"fontFace"` in the `cmd.exe` profile overrides the
|
||||
1. Download the [Debian JPG logo](https://www.debian.org/logos/openlogo-100.jpg)
|
||||
2. Put the image in the
|
||||
`$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>\LocalState\`
|
||||
directory (same directory as your `profiles.json` file).
|
||||
directory (same directory as your `settings.json` file).
|
||||
|
||||
__NOTE__: You can put the image anywhere you like, the above suggestion happens to be convenient.
|
||||
3. Open your WT json properties file.
|
||||
@@ -392,7 +392,7 @@ Notes:
|
||||
|
||||
1. You will need to experiment with different color settings
|
||||
and schemes to make your terminal text visible on top of your image
|
||||
2. If you store the image in the UWP directory (the same directory as your profiles.json file),
|
||||
2. If you store the image in the UWP directory (the same directory as your settings.json file),
|
||||
then you should use the URI style path name given in the above example.
|
||||
More information about UWP URI schemes [here](https://docs.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes).
|
||||
3. Instead of using a UWP URI you can use a:
|
||||
@@ -414,7 +414,7 @@ following objects into your `globals.keybindings` array:
|
||||
{ "command": "paste", "keys": ["ctrl+shift+v"] }
|
||||
```
|
||||
|
||||
> 👉 **Note**: you can also add a keybinding for the `copyTextWithoutNewlines` command. This removes newlines as the text is copied to your clipboard.
|
||||
> 👉 **Note**: you can also add a keybinding for the `copy` command with the argument `"trimWhitespace": true`. This removes newlines as the text is copied to your clipboard.
|
||||
|
||||
This will add copy and paste on <kbd>ctrl+shift+c</kbd>
|
||||
and <kbd>ctrl+shift+v</kbd> respectively.
|
||||
|
||||
@@ -65,7 +65,7 @@ Not currently supported "out of the box". See issue [#1060](https://github.com/m
|
||||
|
||||
## Configuring Windows Terminal
|
||||
|
||||
All Windows Terminal settings are currently managed using the `profiles.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState`.
|
||||
All Windows Terminal settings are currently managed using the `settings.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState`.
|
||||
|
||||
To open the settings file from Windows Terminal:
|
||||
|
||||
@@ -73,7 +73,7 @@ To open the settings file from Windows Terminal:
|
||||
2. From the dropdown list, click `Settings`. You can also use a shortcut: <kbd>Ctrl</kbd>+<kbd>,</kbd>.
|
||||
3. Your default `json` editor will open the settings file.
|
||||
|
||||
For an introduction to the various settings, see [Using Json Settings](UsingJsonSettings.md). The list of valid settings can be found in the [profiles.json documentation](../cascadia/SettingsSchema.md) section.
|
||||
For an introduction to the various settings, see [Using Json Settings](UsingJsonSettings.md). The list of valid settings can be found in the [settings.json documentation](../cascadia/SettingsSchema.md) section.
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
|
||||
@@ -590,7 +590,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, myArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
}
|
||||
{
|
||||
@@ -650,7 +650,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, myArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
|
||||
@@ -704,7 +704,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, myArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
|
||||
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
|
||||
VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty());
|
||||
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
|
||||
|
||||
@@ -170,21 +170,18 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "command": "copy", "keys": ["ctrl+c"] },
|
||||
{ "command": "copyTextWithoutNewlines", "keys": ["alt+c"] },
|
||||
{ "command": { "action": "copy", "trimWhitespace": false }, "keys": ["ctrl+shift+c"] },
|
||||
{ "command": { "action": "copy", "trimWhitespace": true }, "keys": ["alt+shift+c"] },
|
||||
{ "command": { "action": "copy", "singleLine": false }, "keys": ["ctrl+shift+c"] },
|
||||
{ "command": { "action": "copy", "singleLine": true }, "keys": ["alt+shift+c"] },
|
||||
|
||||
{ "command": "newTab", "keys": ["ctrl+t"] },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": ["ctrl+shift+t"] },
|
||||
{ "command": "newTabProfile0", "keys": ["alt+shift+t"] },
|
||||
{ "command": { "action": "newTab", "index": 11 }, "keys": ["ctrl+shift+y"] },
|
||||
{ "command": "newTabProfile8", "keys": ["alt+shift+y"] },
|
||||
|
||||
{ "command": { "action": "copy", "madeUpBool": true }, "keys": ["ctrl+b"] },
|
||||
{ "command": { "action": "copy" }, "keys": ["ctrl+shift+b"] },
|
||||
|
||||
{ "command": "increaseFontSize", "keys": ["ctrl+f"] },
|
||||
{ "command": "decreaseFontSize", "keys": ["ctrl+g"] }
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": ["ctrl+f"] },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 }, "keys": ["ctrl+g"] }
|
||||
|
||||
])" };
|
||||
|
||||
@@ -194,28 +191,17 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(13u, appKeyBindings->_keyShortcuts.size());
|
||||
VERIFY_ARE_EQUAL(10u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` without args parses as Copy(TrimWhitespace=true)"));
|
||||
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(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copyTextWithoutNewlines` parses as Copy(TrimWhitespace=false)"));
|
||||
KeyChord kc{ false, true, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.TrimWhitespace());
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
|
||||
{
|
||||
@@ -226,7 +212,7 @@ namespace TerminalAppLocalTests
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.TrimWhitespace());
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
|
||||
{
|
||||
@@ -237,7 +223,7 @@ namespace TerminalAppLocalTests
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
|
||||
VERIFY_IS_TRUE(realArgs.SingleLine());
|
||||
}
|
||||
|
||||
{
|
||||
@@ -265,19 +251,6 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_ARE_EQUAL(0, realArgs.TerminalArgs().ProfileIndex().Value());
|
||||
}
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTabProfile0` parses as NewTab(Index=0)"));
|
||||
KeyChord kc{ false, true, true, static_cast<int32_t>('T') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTabProfile0, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_ARE_EQUAL(0, realArgs.TerminalArgs().ProfileIndex().Value());
|
||||
}
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` with an index greater than the legacy "
|
||||
@@ -292,19 +265,6 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_ARE_EQUAL(11, realArgs.TerminalArgs().ProfileIndex().Value());
|
||||
}
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTabProfile8` parses as NewTab(Index=8)"));
|
||||
KeyChord kc{ false, true, true, static_cast<int32_t>('Y') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTabProfile8, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
|
||||
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().ProfileIndex());
|
||||
VERIFY_ARE_EQUAL(8, realArgs.TerminalArgs().ProfileIndex().Value());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
@@ -315,7 +275,7 @@ namespace TerminalAppLocalTests
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
|
||||
{
|
||||
@@ -327,15 +287,15 @@ namespace TerminalAppLocalTests
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `increaseFontSize` without args parses as AdjustFontSize(Delta=1)"));
|
||||
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(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::IncreaseFontSize, actionAndArgs.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
@@ -344,10 +304,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `decreaseFontSize` without args parses as AdjustFontSize(Delta=-1)"));
|
||||
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(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::DecreaseFontSize, actionAndArgs.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
@@ -358,8 +318,6 @@ namespace TerminalAppLocalTests
|
||||
void KeyBindingsTests::TestSplitPaneArgs()
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "keys": ["ctrl+a"], "command": "splitVertical" },
|
||||
{ "keys": ["ctrl+b"], "command": "splitHorizontal" },
|
||||
{ "keys": ["ctrl+c"], "command": { "action": "splitPane", "split": null } },
|
||||
{ "keys": ["ctrl+d"], "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": ["ctrl+e"], "command": { "action": "splitPane", "split": "horizontal" } },
|
||||
@@ -375,26 +333,8 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(9u, appKeyBindings->_keyShortcuts.size());
|
||||
VERIFY_ARE_EQUAL(7u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('A') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitVertical, 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(winrt::TerminalApp::SplitState::Vertical, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('B') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitHorizontal, 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(winrt::TerminalApp::SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
@@ -402,7 +342,7 @@ namespace TerminalAppLocalTests
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::None, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
@@ -429,7 +369,7 @@ namespace TerminalAppLocalTests
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::None, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
@@ -438,7 +378,7 @@ namespace TerminalAppLocalTests
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::None, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
@@ -456,7 +396,7 @@ namespace TerminalAppLocalTests
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::None, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,7 +420,7 @@ namespace TerminalAppLocalTests
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
|
||||
VERIFY_IS_FALSE(realArgs.SingleLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,6 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(TestInvalidColorSchemeName);
|
||||
TEST_METHOD(TestHelperFunctions);
|
||||
|
||||
TEST_METHOD(TestLayerGlobalsOnRoot);
|
||||
|
||||
TEST_METHOD(TestProfileIconWithEnvVar);
|
||||
TEST_METHOD(TestProfileBackgroundImageWithEnvVar);
|
||||
|
||||
@@ -160,9 +158,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
const std::string goodProfiles{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
@@ -177,9 +173,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
const std::string badProfiles{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
@@ -194,9 +188,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
const std::string noDefaultAtAll{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"alwaysShowTabs": true
|
||||
},
|
||||
"alwaysShowTabs": true,
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
@@ -388,9 +380,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
const std::string badProfiles{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
@@ -438,21 +428,17 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"alwaysShowTabs": true,
|
||||
"initialCols" : 120,
|
||||
"initialRows" : 30,
|
||||
"rowsToScroll" : 4
|
||||
}
|
||||
"alwaysShowTabs": true,
|
||||
"initialCols" : 120,
|
||||
"initialRows" : 30,
|
||||
"rowsToScroll" : 4
|
||||
})" };
|
||||
const std::string settings1String{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"showTabsInTitlebar": false,
|
||||
"initialCols" : 240,
|
||||
"initialRows" : 60,
|
||||
"rowsToScroll" : 8
|
||||
}
|
||||
"showTabsInTitlebar": false,
|
||||
"initialCols" : 240,
|
||||
"initialRows" : 60,
|
||||
"rowsToScroll" : 8
|
||||
})" };
|
||||
const auto settings0Json = VerifyParseSucceeded(settings0String);
|
||||
const auto settings1Json = VerifyParseSucceeded(settings1String);
|
||||
@@ -1348,114 +1334,6 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(name2, prof2->GetName());
|
||||
}
|
||||
|
||||
void SettingsTests::TestLayerGlobalsOnRoot()
|
||||
{
|
||||
// Test for microsoft/terminal#2906. We added the ability for the root
|
||||
// to be used as the globals object in #2515. However, if you have a
|
||||
// globals object, then the settings in the root would get ignored.
|
||||
// This test ensures that settings from a child "globals" element
|
||||
// _layer_ on top of root properties, and they don't cause the root
|
||||
// properties to be totally ignored.
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"initialRows": 123
|
||||
}
|
||||
})" };
|
||||
const std::string settings1String{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"initialRows": 234
|
||||
})" };
|
||||
const std::string settings2String{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"initialRows": 345,
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
// initialRows should not be cleared here
|
||||
}
|
||||
})" };
|
||||
const std::string settings3String{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"globals": {
|
||||
"initialRows": 456
|
||||
// defaultProfile should not be cleared here
|
||||
}
|
||||
})" };
|
||||
const std::string settings4String{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string settings5String{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
|
||||
}
|
||||
})" };
|
||||
|
||||
VerifyParseSucceeded(settings0String);
|
||||
VerifyParseSucceeded(settings1String);
|
||||
VerifyParseSucceeded(settings2String);
|
||||
VerifyParseSucceeded(settings3String);
|
||||
VerifyParseSucceeded(settings4String);
|
||||
VerifyParseSucceeded(settings5String);
|
||||
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings0String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid1, settings._globals._defaultProfile);
|
||||
VERIFY_ARE_EQUAL(123, settings._globals._initialRows);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings1String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid1, settings._globals._defaultProfile);
|
||||
VERIFY_ARE_EQUAL(234, settings._globals._initialRows);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings2String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid1, settings._globals._defaultProfile);
|
||||
VERIFY_ARE_EQUAL(345, settings._globals._initialRows);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings3String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid2, settings._globals._defaultProfile);
|
||||
VERIFY_ARE_EQUAL(456, settings._globals._initialRows);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings4String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid1, settings._globals._defaultProfile);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings5String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid3, settings._globals._defaultProfile);
|
||||
}
|
||||
}
|
||||
void SettingsTests::TestProfileIconWithEnvVar()
|
||||
{
|
||||
const auto expectedPath = wil::ExpandEnvironmentStringsW<std::wstring>(L"%WINDIR%\\System32\\x_80.png");
|
||||
@@ -2311,9 +2189,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
const std::string badSettings{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
|
||||
@@ -277,219 +277,227 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void TabTests::TryDuplicateBadTab()
|
||||
{
|
||||
// * Create a tab with a profile with GUID 1
|
||||
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// * Try calling _DuplicateTabViewItem on tab 1
|
||||
// * No new tab should be created (and more importantly, the app should not crash)
|
||||
//
|
||||
// Created to test GH#2455
|
||||
Log::Comment(L"This test regressed recently - it is temporarily disabled while GH#5169 is investigated");
|
||||
Log::Result(WEX::Logging::TestResults::Skipped);
|
||||
return;
|
||||
|
||||
const std::string settingsJson0{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
// // * Create a tab with a profile with GUID 1
|
||||
// // * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// // * Try calling _DuplicateTabViewItem on tab 1
|
||||
// // * No new tab should be created (and more importantly, the app should not crash)
|
||||
// //
|
||||
// // Created to test GH#2455
|
||||
|
||||
const std::string settingsJson1{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
// const std::string settingsJson0{ R"(
|
||||
// {
|
||||
// "defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
// "profiles": [
|
||||
// {
|
||||
// "name" : "profile0",
|
||||
// "guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
// "historySize": 1
|
||||
// },
|
||||
// {
|
||||
// "name" : "profile1",
|
||||
// "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
// "historySize": 2
|
||||
// }
|
||||
// ]
|
||||
// })" };
|
||||
|
||||
VerifyParseSucceeded(settingsJson0);
|
||||
auto settings0 = std::make_shared<CascadiaSettings>(false);
|
||||
VERIFY_IS_NOT_NULL(settings0);
|
||||
settings0->_ParseJsonString(settingsJson0, false);
|
||||
settings0->LayerJson(settings0->_userSettings);
|
||||
settings0->_ValidateSettings();
|
||||
// const std::string settingsJson1{ R"(
|
||||
// {
|
||||
// "defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
// "profiles": [
|
||||
// {
|
||||
// "name" : "profile1",
|
||||
// "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
// "historySize": 2
|
||||
// }
|
||||
// ]
|
||||
// })" };
|
||||
|
||||
VerifyParseSucceeded(settingsJson1);
|
||||
auto settings1 = std::make_shared<CascadiaSettings>(false);
|
||||
VERIFY_IS_NOT_NULL(settings1);
|
||||
settings1->_ParseJsonString(settingsJson1, false);
|
||||
settings1->LayerJson(settings1->_userSettings);
|
||||
settings1->_ValidateSettings();
|
||||
// VerifyParseSucceeded(settingsJson0);
|
||||
// auto settings0 = std::make_shared<CascadiaSettings>(false);
|
||||
// VERIFY_IS_NOT_NULL(settings0);
|
||||
// settings0->_ParseJsonString(settingsJson0, false);
|
||||
// settings0->LayerJson(settings0->_userSettings);
|
||||
// settings0->_ValidateSettings();
|
||||
|
||||
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
// VerifyParseSucceeded(settingsJson1);
|
||||
// auto settings1 = std::make_shared<CascadiaSettings>(false);
|
||||
// VERIFY_IS_NOT_NULL(settings1);
|
||||
// settings1->_ParseJsonString(settingsJson1, false);
|
||||
// settings1->LayerJson(settings1->_userSettings);
|
||||
// settings1->_ValidateSettings();
|
||||
|
||||
// This is super wacky, but we can't just initialize the
|
||||
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
||||
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
|
||||
// during TerminalPage::Create() below.
|
||||
//
|
||||
// Instead, create the winrt object, then get a com_ptr to the
|
||||
// implementation _from_ the winrt object. This seems to work, even if
|
||||
// it's weird.
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
||||
_initializeTerminalPage(page, settings0);
|
||||
// const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
// const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
// const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
auto result = RunOnUIThread([&page]() {
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
// // This is super wacky, but we can't just initialize the
|
||||
// // com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
||||
// // the lambda. We'll crash trying to get a weak_ref to the TerminalPage
|
||||
// // during TerminalPage::Create() below.
|
||||
// //
|
||||
// // Instead, create the winrt object, then get a com_ptr to the
|
||||
// // implementation _from_ the winrt object. This seems to work, even if
|
||||
// // it's weird.
|
||||
// winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
||||
// _initializeTerminalPage(page, settings0);
|
||||
|
||||
Log::Comment(L"Duplicate the first tab");
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_DuplicateTabViewItem();
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
// auto result = RunOnUIThread([&page]() {
|
||||
// VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Change the settings of the TerminalPage so the first profile is "
|
||||
L"no longer in the list of profiles"));
|
||||
result = RunOnUIThread([&page, settings1]() {
|
||||
page->_settings = settings1;
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
// Log::Comment(L"Duplicate the first tab");
|
||||
// result = RunOnUIThread([&page]() {
|
||||
// page->_DuplicateTabViewItem();
|
||||
// VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
|
||||
Log::Comment(L"Duplicate the tab, and don't crash");
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_DuplicateTabViewItem();
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
// Log::Comment(NoThrowString().Format(
|
||||
// L"Change the settings of the TerminalPage so the first profile is "
|
||||
// L"no longer in the list of profiles"));
|
||||
// result = RunOnUIThread([&page, settings1]() {
|
||||
// page->_settings = settings1;
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
|
||||
// Log::Comment(L"Duplicate the tab, and don't crash");
|
||||
// result = RunOnUIThread([&page]() {
|
||||
// page->_DuplicateTabViewItem();
|
||||
// VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
void TabTests::TryDuplicateBadPane()
|
||||
{
|
||||
// * Create a tab with a profile with GUID 1
|
||||
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// * Try calling _SplitPane(Duplicate) on tab 1
|
||||
// * No new pane should be created (and more importantly, the app should not crash)
|
||||
//
|
||||
// Created to test GH#2455
|
||||
Log::Comment(L"This test regressed recently - it is temporarily disabled while GH#5169 is investigated");
|
||||
Log::Result(WEX::Logging::TestResults::Skipped);
|
||||
return;
|
||||
|
||||
const std::string settingsJson0{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
// // * Create a tab with a profile with GUID 1
|
||||
// // * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// // * Try calling _SplitPane(Duplicate) on tab 1
|
||||
// // * No new pane should be created (and more importantly, the app should not crash)
|
||||
// //
|
||||
// // Created to test GH#2455
|
||||
|
||||
const std::string settingsJson1{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
// const std::string settingsJson0{ R"(
|
||||
// {
|
||||
// "defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
// "profiles": [
|
||||
// {
|
||||
// "name" : "profile0",
|
||||
// "guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
// "historySize": 1
|
||||
// },
|
||||
// {
|
||||
// "name" : "profile1",
|
||||
// "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
// "historySize": 2
|
||||
// }
|
||||
// ]
|
||||
// })" };
|
||||
|
||||
VerifyParseSucceeded(settingsJson0);
|
||||
auto settings0 = std::make_shared<CascadiaSettings>(false);
|
||||
VERIFY_IS_NOT_NULL(settings0);
|
||||
settings0->_ParseJsonString(settingsJson0, false);
|
||||
settings0->LayerJson(settings0->_userSettings);
|
||||
settings0->_ValidateSettings();
|
||||
// const std::string settingsJson1{ R"(
|
||||
// {
|
||||
// "defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
// "profiles": [
|
||||
// {
|
||||
// "name" : "profile1",
|
||||
// "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
// "historySize": 2
|
||||
// }
|
||||
// ]
|
||||
// })" };
|
||||
|
||||
VerifyParseSucceeded(settingsJson1);
|
||||
auto settings1 = std::make_shared<CascadiaSettings>(false);
|
||||
VERIFY_IS_NOT_NULL(settings1);
|
||||
settings1->_ParseJsonString(settingsJson1, false);
|
||||
settings1->LayerJson(settings1->_userSettings);
|
||||
settings1->_ValidateSettings();
|
||||
// VerifyParseSucceeded(settingsJson0);
|
||||
// auto settings0 = std::make_shared<CascadiaSettings>(false);
|
||||
// VERIFY_IS_NOT_NULL(settings0);
|
||||
// settings0->_ParseJsonString(settingsJson0, false);
|
||||
// settings0->LayerJson(settings0->_userSettings);
|
||||
// settings0->_ValidateSettings();
|
||||
|
||||
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
// VerifyParseSucceeded(settingsJson1);
|
||||
// auto settings1 = std::make_shared<CascadiaSettings>(false);
|
||||
// VERIFY_IS_NOT_NULL(settings1);
|
||||
// settings1->_ParseJsonString(settingsJson1, false);
|
||||
// settings1->LayerJson(settings1->_userSettings);
|
||||
// settings1->_ValidateSettings();
|
||||
|
||||
// This is super wacky, but we can't just initialize the
|
||||
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
||||
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
|
||||
// during TerminalPage::Create() below.
|
||||
//
|
||||
// Instead, create the winrt object, then get a com_ptr to the
|
||||
// implementation _from_ the winrt object. This seems to work, even if
|
||||
// it's weird.
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
||||
_initializeTerminalPage(page, settings0);
|
||||
// const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
// const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
// const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
auto result = RunOnUIThread([&page]() {
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
// // This is super wacky, but we can't just initialize the
|
||||
// // com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
||||
// // the lambda. We'll crash trying to get a weak_ref to the TerminalPage
|
||||
// // during TerminalPage::Create() below.
|
||||
// //
|
||||
// // Instead, create the winrt object, then get a com_ptr to the
|
||||
// // implementation _from_ the winrt object. This seems to work, even if
|
||||
// // it's weird.
|
||||
// winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
||||
// _initializeTerminalPage(page, settings0);
|
||||
|
||||
result = RunOnUIThread([&page]() {
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetStrongTabImpl(0);
|
||||
VERIFY_ARE_EQUAL(1, tab->_GetLeafPaneCount());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
// auto result = RunOnUIThread([&page]() {
|
||||
// VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
|
||||
Log::Comment(NoThrowString().Format(L"Duplicate the first pane"));
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
|
||||
// result = RunOnUIThread([&page]() {
|
||||
// VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
// auto tab = page->_GetStrongTabImpl(0);
|
||||
// VERIFY_ARE_EQUAL(1, tab->_GetLeafPaneCount());
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetStrongTabImpl(0);
|
||||
VERIFY_ARE_EQUAL(2, tab->_GetLeafPaneCount());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
// Log::Comment(NoThrowString().Format(L"Duplicate the first pane"));
|
||||
// result = RunOnUIThread([&page]() {
|
||||
// page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Change the settings of the TerminalPage so the first profile is "
|
||||
L"no longer in the list of profiles"));
|
||||
result = RunOnUIThread([&page, settings1]() {
|
||||
page->_settings = settings1;
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
// VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
// auto tab = page->_GetStrongTabImpl(0);
|
||||
// VERIFY_ARE_EQUAL(2, tab->_GetLeafPaneCount());
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
|
||||
Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash"));
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
|
||||
// Log::Comment(NoThrowString().Format(
|
||||
// L"Change the settings of the TerminalPage so the first profile is "
|
||||
// L"no longer in the list of profiles"));
|
||||
// result = RunOnUIThread([&page, settings1]() {
|
||||
// page->_settings = settings1;
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetStrongTabImpl(0);
|
||||
VERIFY_ARE_EQUAL(2,
|
||||
tab->_GetLeafPaneCount(),
|
||||
L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
// Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash"));
|
||||
// result = RunOnUIThread([&page]() {
|
||||
// page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
|
||||
|
||||
auto cleanup = wil::scope_exit([] {
|
||||
auto result = RunOnUIThread([]() {
|
||||
// There's something causing us to crash north of
|
||||
// TSFInputControl::NotifyEnter, or LayoutRequested. It's very
|
||||
// unclear what that issue is. Since these tests don't run in
|
||||
// CI, simply log a message so that the dev running these tests
|
||||
// knows it's expected.
|
||||
Log::Comment(L"This test often crashes on cleanup, even when it succeeds. If it succeeded, then crashes, that's okay.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
});
|
||||
// VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
// auto tab = page->_GetStrongTabImpl(0);
|
||||
// VERIFY_ARE_EQUAL(2,
|
||||
// tab->_GetLeafPaneCount(),
|
||||
// L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
|
||||
// auto cleanup = wil::scope_exit([] {
|
||||
// auto result = RunOnUIThread([]() {
|
||||
// // There's something causing us to crash north of
|
||||
// // TSFInputControl::NotifyEnter, or LayoutRequested. It's very
|
||||
// // unclear what that issue is. Since these tests don't run in
|
||||
// // CI, simply log a message so that the dev running these tests
|
||||
// // knows it's expected.
|
||||
// Log::Comment(L"This test often crashes on cleanup, even when it succeeds. If it succeeded, then crashes, that's okay.");
|
||||
// });
|
||||
// VERIFY_SUCCEEDED(result);
|
||||
// });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -427,10 +427,8 @@ const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
return returnText.release();
|
||||
}
|
||||
|
||||
void _stdcall TerminalSendKeyEvent(void* terminal, WPARAM wParam)
|
||||
static ControlKeyStates getControlKeyState() noexcept
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
const auto scanCode = MapVirtualKeyW((UINT)wParam, MAPVK_VK_TO_VSC);
|
||||
struct KeyModifier
|
||||
{
|
||||
int vkey;
|
||||
@@ -458,10 +456,17 @@ void _stdcall TerminalSendKeyEvent(void* terminal, WPARAM wParam)
|
||||
}
|
||||
}
|
||||
|
||||
publicTerminal->_terminal->SendKeyEvent((WORD)wParam, (WORD)scanCode, flags);
|
||||
return flags;
|
||||
}
|
||||
|
||||
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch)
|
||||
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
const auto flags = getControlKeyState();
|
||||
publicTerminal->_terminal->SendKeyEvent(vkey, scanCode, flags);
|
||||
}
|
||||
|
||||
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode)
|
||||
{
|
||||
if (ch == '\t')
|
||||
{
|
||||
@@ -469,7 +474,8 @@ void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch)
|
||||
}
|
||||
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
publicTerminal->_terminal->SendCharEvent(ch);
|
||||
const auto flags = getControlKeyState();
|
||||
publicTerminal->_terminal->SendCharEvent(ch, scanCode, flags);
|
||||
}
|
||||
|
||||
void _stdcall DestroyTerminal(void* terminal)
|
||||
|
||||
@@ -34,8 +34,8 @@ __declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
__declspec(dllexport) void _stdcall DestroyTerminal(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
|
||||
__declspec(dllexport) void _stdcall TerminalRegisterWriteCallback(void* terminal, const void __stdcall callback(wchar_t*));
|
||||
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WPARAM wParam);
|
||||
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch);
|
||||
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode);
|
||||
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
|
||||
__declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
|
||||
};
|
||||
@@ -82,8 +82,8 @@ private:
|
||||
friend void _stdcall TerminalClearSelection(void* terminal);
|
||||
friend const wchar_t* _stdcall TerminalGetSelection(void* terminal);
|
||||
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
friend void _stdcall TerminalSendKeyEvent(void* terminal, WPARAM wParam);
|
||||
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch);
|
||||
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode);
|
||||
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
|
||||
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
|
||||
friend void _stdcall TerminalBlinkCursor(void* terminal);
|
||||
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
|
||||
|
||||
@@ -93,9 +93,9 @@ namespace winrt::TerminalApp::implementation
|
||||
struct CopyTextArgs : public CopyTextArgsT<CopyTextArgs>
|
||||
{
|
||||
CopyTextArgs() = default;
|
||||
GETSET_PROPERTY(bool, TrimWhitespace, true);
|
||||
GETSET_PROPERTY(bool, SingleLine, false);
|
||||
|
||||
static constexpr std::string_view TrimWhitespaceKey{ "trimWhitespace" };
|
||||
static constexpr std::string_view SingleLineKey{ "singleLine" };
|
||||
|
||||
public:
|
||||
bool Equals(const IActionArgs& other)
|
||||
@@ -103,7 +103,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto otherAsUs = other.try_as<CopyTextArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_TrimWhitespace == _TrimWhitespace;
|
||||
return otherAsUs->_SingleLine == _SingleLine;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -111,9 +111,9 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<CopyTextArgs>();
|
||||
if (auto trimWhitespace{ json[JsonKey(TrimWhitespaceKey)] })
|
||||
if (auto singleLine{ json[JsonKey(SingleLineKey)] })
|
||||
{
|
||||
args->_TrimWhitespace = trimWhitespace.asBool();
|
||||
args->_SingleLine = singleLine.asBool();
|
||||
}
|
||||
return { *args, {} };
|
||||
}
|
||||
@@ -328,7 +328,7 @@ namespace winrt::TerminalApp::implementation
|
||||
return TerminalApp::SplitState::Automatic;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TerminalApp::SplitState::None;
|
||||
return TerminalApp::SplitState::Automatic;
|
||||
};
|
||||
|
||||
// Possible SplitType values
|
||||
@@ -345,7 +345,7 @@ namespace winrt::TerminalApp::implementation
|
||||
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
|
||||
{
|
||||
SplitPaneArgs() = default;
|
||||
GETSET_PROPERTY(winrt::TerminalApp::SplitState, SplitStyle, winrt::TerminalApp::SplitState::None);
|
||||
GETSET_PROPERTY(winrt::TerminalApp::SplitState, SplitStyle, winrt::TerminalApp::SplitState::Automatic);
|
||||
GETSET_PROPERTY(winrt::TerminalApp::NewTerminalArgs, TerminalArgs, nullptr);
|
||||
GETSET_PROPERTY(winrt::TerminalApp::SplitType, SplitMode, winrt::TerminalApp::SplitType::Manual);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace TerminalApp
|
||||
|
||||
[default_interface] runtimeclass CopyTextArgs : IActionArgs
|
||||
{
|
||||
Boolean TrimWhitespace { get; };
|
||||
Boolean SingleLine { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass NewTabArgs : IActionArgs
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::CopyTextArgs>())
|
||||
{
|
||||
const auto handled = _CopyText(realArgs.TrimWhitespace());
|
||||
const auto handled = _CopyText(realArgs.SingleLine());
|
||||
args.Handled(handled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ void AppCommandlineArgs::_buildSplitPaneParser()
|
||||
args->SplitStyle(SplitState::Automatic);
|
||||
// Make sure to use the `Option`s here to check if they were set -
|
||||
// _getNewTerminalArgs might reset them while parsing a commandline
|
||||
if ((*_horizontalOption || *_verticalOption) && (_splitHorizontal))
|
||||
if ((*_horizontalOption || *_verticalOption))
|
||||
{
|
||||
if (_splitHorizontal)
|
||||
{
|
||||
@@ -225,7 +225,7 @@ void AppCommandlineArgs::_buildSplitPaneParser()
|
||||
}
|
||||
else if (_splitVertical)
|
||||
{
|
||||
args->SplitStyle(SplitState::Horizontal);
|
||||
args->SplitStyle(SplitState::Vertical);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,20 +25,10 @@ static constexpr std::string_view ActionKey{ "action" };
|
||||
static constexpr std::string_view UnboundKey{ "unbound" };
|
||||
|
||||
static constexpr std::string_view CopyTextKey{ "copy" };
|
||||
static constexpr std::string_view CopyTextWithoutNewlinesKey{ "copyTextWithoutNewlines" }; // Legacy
|
||||
static constexpr std::string_view PasteTextKey{ "paste" };
|
||||
static constexpr std::string_view OpenNewTabDropdownKey{ "openNewTabDropdown" };
|
||||
static constexpr std::string_view DuplicateTabKey{ "duplicateTab" };
|
||||
static constexpr std::string_view NewTabKey{ "newTab" };
|
||||
static constexpr std::string_view NewTabWithProfile0Key{ "newTabProfile0" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile1Key{ "newTabProfile1" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile2Key{ "newTabProfile2" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile3Key{ "newTabProfile3" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile4Key{ "newTabProfile4" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile5Key{ "newTabProfile5" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile6Key{ "newTabProfile6" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile7Key{ "newTabProfile7" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile8Key{ "newTabProfile8" }; // Legacy
|
||||
static constexpr std::string_view NewWindowKey{ "newWindow" };
|
||||
static constexpr std::string_view CloseWindowKey{ "closeWindow" };
|
||||
static constexpr std::string_view CloseTabKey{ "closeTab" };
|
||||
@@ -46,37 +36,17 @@ static constexpr std::string_view ClosePaneKey{ "closePane" };
|
||||
static constexpr std::string_view SwitchtoTabKey{ "switchToTab" };
|
||||
static constexpr std::string_view NextTabKey{ "nextTab" };
|
||||
static constexpr std::string_view PrevTabKey{ "prevTab" };
|
||||
static constexpr std::string_view IncreaseFontSizeKey{ "increaseFontSize" };
|
||||
static constexpr std::string_view DecreaseFontSizeKey{ "decreaseFontSize" };
|
||||
static constexpr std::string_view AdjustFontSizeKey{ "adjustFontSize" };
|
||||
static constexpr std::string_view ResetFontSizeKey{ "resetFontSize" };
|
||||
static constexpr std::string_view ScrollupKey{ "scrollUp" };
|
||||
static constexpr std::string_view ScrolldownKey{ "scrollDown" };
|
||||
static constexpr std::string_view ScrolluppageKey{ "scrollUpPage" };
|
||||
static constexpr std::string_view ScrolldownpageKey{ "scrollDownPage" };
|
||||
static constexpr std::string_view SwitchToTabKey{ "switchToTab" };
|
||||
static constexpr std::string_view SwitchToTab0Key{ "switchToTab0" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab1Key{ "switchToTab1" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab2Key{ "switchToTab2" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab3Key{ "switchToTab3" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab4Key{ "switchToTab4" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab5Key{ "switchToTab5" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab6Key{ "switchToTab6" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab7Key{ "switchToTab7" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab8Key{ "switchToTab8" }; // Legacy
|
||||
static constexpr std::string_view OpenSettingsKey{ "openSettings" }; // Legacy
|
||||
static constexpr std::string_view OpenSettingsKey{ "openSettings" }; // TODO GH#2557: Add args for OpenSettings
|
||||
static constexpr std::string_view SplitPaneKey{ "splitPane" };
|
||||
static constexpr std::string_view SplitHorizontalKey{ "splitHorizontal" }; // Legacy
|
||||
static constexpr std::string_view SplitVerticalKey{ "splitVertical" }; // Legacy
|
||||
static constexpr std::string_view ResizePaneKey{ "resizePane" };
|
||||
static constexpr std::string_view ResizePaneLeftKey{ "resizePaneLeft" }; // Legacy
|
||||
static constexpr std::string_view ResizePaneRightKey{ "resizePaneRight" }; // Legacy
|
||||
static constexpr std::string_view ResizePaneUpKey{ "resizePaneUp" }; // Legacy
|
||||
static constexpr std::string_view ResizePaneDownKey{ "resizePaneDown" }; // Legacy
|
||||
static constexpr std::string_view MoveFocusKey{ "moveFocus" };
|
||||
static constexpr std::string_view MoveFocusLeftKey{ "moveFocusLeft" }; // Legacy
|
||||
static constexpr std::string_view MoveFocusRightKey{ "moveFocusRight" }; // Legacy
|
||||
static constexpr std::string_view MoveFocusUpKey{ "moveFocusUp" }; // Legacy
|
||||
static constexpr std::string_view MoveFocusDownKey{ "moveFocusDown" }; // Legacy
|
||||
static constexpr std::string_view FindKey{ "find" };
|
||||
static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
|
||||
|
||||
@@ -90,55 +60,25 @@ static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
|
||||
// about here.
|
||||
static const std::map<std::string_view, ShortcutAction, std::less<>> commandNames{
|
||||
{ CopyTextKey, ShortcutAction::CopyText },
|
||||
{ CopyTextWithoutNewlinesKey, ShortcutAction::CopyTextWithoutNewlines },
|
||||
{ PasteTextKey, ShortcutAction::PasteText },
|
||||
{ OpenNewTabDropdownKey, ShortcutAction::OpenNewTabDropdown },
|
||||
{ DuplicateTabKey, ShortcutAction::DuplicateTab },
|
||||
{ NewTabKey, ShortcutAction::NewTab },
|
||||
{ NewTabWithProfile0Key, ShortcutAction::NewTabProfile0 },
|
||||
{ NewTabWithProfile1Key, ShortcutAction::NewTabProfile1 },
|
||||
{ NewTabWithProfile2Key, ShortcutAction::NewTabProfile2 },
|
||||
{ NewTabWithProfile3Key, ShortcutAction::NewTabProfile3 },
|
||||
{ NewTabWithProfile4Key, ShortcutAction::NewTabProfile4 },
|
||||
{ NewTabWithProfile5Key, ShortcutAction::NewTabProfile5 },
|
||||
{ NewTabWithProfile6Key, ShortcutAction::NewTabProfile6 },
|
||||
{ NewTabWithProfile7Key, ShortcutAction::NewTabProfile7 },
|
||||
{ NewTabWithProfile8Key, ShortcutAction::NewTabProfile8 },
|
||||
{ NewWindowKey, ShortcutAction::NewWindow },
|
||||
{ CloseWindowKey, ShortcutAction::CloseWindow },
|
||||
{ CloseTabKey, ShortcutAction::CloseTab },
|
||||
{ ClosePaneKey, ShortcutAction::ClosePane },
|
||||
{ NextTabKey, ShortcutAction::NextTab },
|
||||
{ PrevTabKey, ShortcutAction::PrevTab },
|
||||
{ IncreaseFontSizeKey, ShortcutAction::IncreaseFontSize },
|
||||
{ DecreaseFontSizeKey, ShortcutAction::DecreaseFontSize },
|
||||
{ AdjustFontSizeKey, ShortcutAction::AdjustFontSize },
|
||||
{ ResetFontSizeKey, ShortcutAction::ResetFontSize },
|
||||
{ ScrollupKey, ShortcutAction::ScrollUp },
|
||||
{ ScrolldownKey, ShortcutAction::ScrollDown },
|
||||
{ ScrolluppageKey, ShortcutAction::ScrollUpPage },
|
||||
{ ScrolldownpageKey, ShortcutAction::ScrollDownPage },
|
||||
{ SwitchToTabKey, ShortcutAction::SwitchToTab },
|
||||
{ SwitchToTab0Key, ShortcutAction::SwitchToTab0 },
|
||||
{ SwitchToTab1Key, ShortcutAction::SwitchToTab1 },
|
||||
{ SwitchToTab2Key, ShortcutAction::SwitchToTab2 },
|
||||
{ SwitchToTab3Key, ShortcutAction::SwitchToTab3 },
|
||||
{ SwitchToTab4Key, ShortcutAction::SwitchToTab4 },
|
||||
{ SwitchToTab5Key, ShortcutAction::SwitchToTab5 },
|
||||
{ SwitchToTab6Key, ShortcutAction::SwitchToTab6 },
|
||||
{ SwitchToTab7Key, ShortcutAction::SwitchToTab7 },
|
||||
{ SwitchToTab8Key, ShortcutAction::SwitchToTab8 },
|
||||
{ SplitHorizontalKey, ShortcutAction::SplitHorizontal },
|
||||
{ SplitVerticalKey, ShortcutAction::SplitVertical },
|
||||
{ ResizePaneKey, ShortcutAction::ResizePane },
|
||||
{ ResizePaneLeftKey, ShortcutAction::ResizePaneLeft },
|
||||
{ ResizePaneRightKey, ShortcutAction::ResizePaneRight },
|
||||
{ ResizePaneUpKey, ShortcutAction::ResizePaneUp },
|
||||
{ ResizePaneDownKey, ShortcutAction::ResizePaneDown },
|
||||
{ MoveFocusKey, ShortcutAction::MoveFocus },
|
||||
{ MoveFocusLeftKey, ShortcutAction::MoveFocusLeft },
|
||||
{ MoveFocusRightKey, ShortcutAction::MoveFocusRight },
|
||||
{ MoveFocusUpKey, ShortcutAction::MoveFocusUp },
|
||||
{ MoveFocusDownKey, ShortcutAction::MoveFocusDown },
|
||||
{ OpenSettingsKey, ShortcutAction::OpenSettings },
|
||||
{ ToggleFullscreenKey, ShortcutAction::ToggleFullscreen },
|
||||
{ SplitPaneKey, ShortcutAction::SplitPane },
|
||||
@@ -149,146 +89,6 @@ static const std::map<std::string_view, ShortcutAction, std::less<>> commandName
|
||||
using ParseResult = std::tuple<IActionArgs, std::vector<TerminalApp::SettingsLoadWarnings>>;
|
||||
using ParseActionFunction = std::function<ParseResult(const Json::Value&)>;
|
||||
|
||||
// Function Description:
|
||||
// - Creates a function that can be used to generate a SplitPaneArgs for the
|
||||
// legacy Split[SplitState] actions. These actions don't accept args from
|
||||
// json, instead, they just return a SplitPaneArgs with the style already
|
||||
// pre-defined, based on the input param.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - style: the split style to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into one of the legacy
|
||||
// Split[SplitState] args.
|
||||
ParseActionFunction LegacyParseSplitPaneArgs(SplitState style)
|
||||
{
|
||||
auto pfn = [style](const Json::Value & /*value*/) -> ParseResult {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::SplitPaneArgs>();
|
||||
args->SplitStyle(style);
|
||||
return { *args, {} };
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Creates a function that can be used to generate a MoveFocusArgs for the
|
||||
// legacy MoveFocus[Direction] actions. These actions don't accept args from
|
||||
// json, instead, they just return a MoveFocusArgs with the Direction already
|
||||
// per-defined, based on the input param.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - direction: the direction to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into one of the legacy
|
||||
// MoveFocus[Direction] args.
|
||||
ParseActionFunction LegacyParseMoveFocusArgs(Direction direction)
|
||||
{
|
||||
auto pfn = [direction](const Json::Value & /*value*/) -> ParseResult {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::MoveFocusArgs>();
|
||||
args->Direction(direction);
|
||||
return { *args, {} };
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Creates a function that can be used to generate a ResizePaneArgs for the
|
||||
// legacy ResizePane[Direction] actions. These actions don't accept args from
|
||||
// json, instead, they just return a ResizePaneArgs with the Direction already
|
||||
// per-defined, based on the input param.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - direction: the direction to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into one of the legacy
|
||||
// ResizePane[Direction] args.
|
||||
ParseActionFunction LegacyParseResizePaneArgs(Direction direction)
|
||||
{
|
||||
auto pfn = [direction](const Json::Value & /*value*/) -> ParseResult {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::ResizePaneArgs>();
|
||||
args->Direction(direction);
|
||||
return { *args, {} };
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Creates a function that can be used to generate a NewTabWithProfileArgs for
|
||||
// the legacy NewTabWithProfile[Index] actions. These actions don't accept
|
||||
// args from json, instead, they just return a NewTabWithProfileArgs with the
|
||||
// index already per-defined, based on the input param.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - index: the profile index to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into one of the legacy
|
||||
// NewTabWithProfile[Index] args.
|
||||
ParseActionFunction LegacyParseNewTabWithProfileArgs(int index)
|
||||
{
|
||||
auto pfn = [index](const Json::Value & /*value*/) -> ParseResult {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::NewTabArgs>();
|
||||
auto newTerminalArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTerminalArgs>();
|
||||
newTerminalArgs->ProfileIndex(index);
|
||||
args->TerminalArgs(*newTerminalArgs);
|
||||
return { *args, {} };
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Creates a function that can be used to generate a SwitchToTabArgs for the
|
||||
// legacy SwitchToTab[Index] actions. These actions don't accept args from
|
||||
// json, instead, they just return a SwitchToTabArgs with the index already
|
||||
// per-defined, based on the input param.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - index: the tab index to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into one of the legacy
|
||||
// SwitchToTab[Index] args.
|
||||
ParseActionFunction LegacyParseSwitchToTabArgs(int index)
|
||||
{
|
||||
auto pfn = [index](const Json::Value & /*value*/) -> ParseResult {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::SwitchToTabArgs>();
|
||||
args->TabIndex(index);
|
||||
return { *args, {} };
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Used to generate a CopyTextArgs for the legacy CopyTextWithoutNewlines
|
||||
// action.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - direction: the direction to create the parse function for.
|
||||
// Return Value:
|
||||
// - A CopyTextArgs with TrimWhitespace set to true, to emulate "CopyTextWithoutNewlines".
|
||||
ParseResult LegacyParseCopyTextWithoutNewlinesArgs(const Json::Value& /*json*/)
|
||||
{
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::CopyTextArgs>();
|
||||
args->TrimWhitespace(false);
|
||||
return { *args, {} };
|
||||
};
|
||||
|
||||
// Function Description:
|
||||
// - Used to generate a AdjustFontSizeArgs for IncreaseFontSize/DecreaseFontSize
|
||||
// actions with a delta of 1/-1.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - delta: the font size delta to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into an AdjustFontSizeArgs.
|
||||
ParseActionFunction LegacyParseAdjustFontSizeArgs(int delta)
|
||||
{
|
||||
auto pfn = [delta](const Json::Value & /*value*/) -> ParseResult {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::AdjustFontSizeArgs>();
|
||||
args->Delta(delta);
|
||||
return { *args, {} };
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// This is a map of ShortcutAction->function<IActionArgs(Json::Value)>. It holds
|
||||
// a set of deserializer functions that can be used to deserialize a IActionArgs
|
||||
// from json. Each type of IActionArgs that can accept arbitrary args should be
|
||||
@@ -296,48 +96,18 @@ ParseActionFunction LegacyParseAdjustFontSizeArgs(int delta)
|
||||
// value.
|
||||
static const std::map<ShortcutAction, ParseActionFunction, std::less<>> argParsers{
|
||||
{ ShortcutAction::CopyText, winrt::TerminalApp::implementation::CopyTextArgs::FromJson },
|
||||
{ ShortcutAction::CopyTextWithoutNewlines, LegacyParseCopyTextWithoutNewlinesArgs },
|
||||
|
||||
{ ShortcutAction::NewTab, winrt::TerminalApp::implementation::NewTabArgs::FromJson },
|
||||
{ ShortcutAction::NewTabProfile0, LegacyParseNewTabWithProfileArgs(0) },
|
||||
{ ShortcutAction::NewTabProfile1, LegacyParseNewTabWithProfileArgs(1) },
|
||||
{ ShortcutAction::NewTabProfile2, LegacyParseNewTabWithProfileArgs(2) },
|
||||
{ ShortcutAction::NewTabProfile3, LegacyParseNewTabWithProfileArgs(3) },
|
||||
{ ShortcutAction::NewTabProfile4, LegacyParseNewTabWithProfileArgs(4) },
|
||||
{ ShortcutAction::NewTabProfile5, LegacyParseNewTabWithProfileArgs(5) },
|
||||
{ ShortcutAction::NewTabProfile6, LegacyParseNewTabWithProfileArgs(6) },
|
||||
{ ShortcutAction::NewTabProfile7, LegacyParseNewTabWithProfileArgs(7) },
|
||||
{ ShortcutAction::NewTabProfile8, LegacyParseNewTabWithProfileArgs(8) },
|
||||
|
||||
{ ShortcutAction::SwitchToTab, winrt::TerminalApp::implementation::SwitchToTabArgs::FromJson },
|
||||
{ ShortcutAction::SwitchToTab0, LegacyParseSwitchToTabArgs(0) },
|
||||
{ ShortcutAction::SwitchToTab1, LegacyParseSwitchToTabArgs(1) },
|
||||
{ ShortcutAction::SwitchToTab2, LegacyParseSwitchToTabArgs(2) },
|
||||
{ ShortcutAction::SwitchToTab3, LegacyParseSwitchToTabArgs(3) },
|
||||
{ ShortcutAction::SwitchToTab4, LegacyParseSwitchToTabArgs(4) },
|
||||
{ ShortcutAction::SwitchToTab5, LegacyParseSwitchToTabArgs(5) },
|
||||
{ ShortcutAction::SwitchToTab6, LegacyParseSwitchToTabArgs(6) },
|
||||
{ ShortcutAction::SwitchToTab7, LegacyParseSwitchToTabArgs(7) },
|
||||
{ ShortcutAction::SwitchToTab8, LegacyParseSwitchToTabArgs(8) },
|
||||
|
||||
{ ShortcutAction::ResizePane, winrt::TerminalApp::implementation::ResizePaneArgs::FromJson },
|
||||
{ ShortcutAction::ResizePaneLeft, LegacyParseResizePaneArgs(Direction::Left) },
|
||||
{ ShortcutAction::ResizePaneRight, LegacyParseResizePaneArgs(Direction::Right) },
|
||||
{ ShortcutAction::ResizePaneUp, LegacyParseResizePaneArgs(Direction::Up) },
|
||||
{ ShortcutAction::ResizePaneDown, LegacyParseResizePaneArgs(Direction::Down) },
|
||||
|
||||
{ ShortcutAction::MoveFocus, winrt::TerminalApp::implementation::MoveFocusArgs::FromJson },
|
||||
{ ShortcutAction::MoveFocusLeft, LegacyParseMoveFocusArgs(Direction::Left) },
|
||||
{ ShortcutAction::MoveFocusRight, LegacyParseMoveFocusArgs(Direction::Right) },
|
||||
{ ShortcutAction::MoveFocusUp, LegacyParseMoveFocusArgs(Direction::Up) },
|
||||
{ ShortcutAction::MoveFocusDown, LegacyParseMoveFocusArgs(Direction::Down) },
|
||||
|
||||
{ ShortcutAction::DecreaseFontSize, LegacyParseAdjustFontSizeArgs(-1) },
|
||||
{ ShortcutAction::IncreaseFontSize, LegacyParseAdjustFontSizeArgs(1) },
|
||||
{ ShortcutAction::AdjustFontSize, winrt::TerminalApp::implementation::AdjustFontSizeArgs::FromJson },
|
||||
|
||||
{ ShortcutAction::SplitPane, winrt::TerminalApp::implementation::SplitPaneArgs::FromJson },
|
||||
{ ShortcutAction::SplitVertical, LegacyParseSplitPaneArgs(SplitState::Vertical) },
|
||||
{ ShortcutAction::SplitHorizontal, LegacyParseSplitPaneArgs(SplitState::Horizontal) },
|
||||
|
||||
{ ShortcutAction::Invalid, nullptr },
|
||||
};
|
||||
|
||||
@@ -138,6 +138,29 @@ catch (...)
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Function Description:
|
||||
// - Get the AppLogic for the current active Xaml application, or null if there isn't one.
|
||||
// Return value:
|
||||
// - A pointer (bare) to the applogic, or nullptr. The app logic outlives all other objects,
|
||||
// unless the application is in a terrible way, so this is "safe."
|
||||
AppLogic* AppLogic::Current() noexcept
|
||||
try
|
||||
{
|
||||
if (auto currentXamlApp{ winrt::Windows::UI::Xaml::Application::Current().try_as<winrt::TerminalApp::App>() })
|
||||
{
|
||||
if (auto appLogicPointer{ winrt::get_self<AppLogic>(currentXamlApp.Logic()) })
|
||||
{
|
||||
return appLogicPointer;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AppLogic::AppLogic() :
|
||||
_dialogLock{},
|
||||
_loadedInitialSettings{ false },
|
||||
@@ -218,7 +241,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_root->Loaded({ this, &AppLogic::_OnLoaded });
|
||||
_root->Create();
|
||||
|
||||
_ApplyTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
_ApplyTheme(_settings->GlobalSettings().GetTheme());
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
@@ -260,7 +283,27 @@ namespace winrt::TerminalApp::implementation
|
||||
// IMPORTANT: Set the requested theme of the dialog, because the
|
||||
// PopupRoot isn't directly in the Xaml tree of our root. So the dialog
|
||||
// won't inherit our RequestedTheme automagically.
|
||||
dialog.RequestedTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
// GH#5195, GH#3654 Because we cannot set RequestedTheme at the application level,
|
||||
// we occasionally run into issues where parts of our UI end up themed incorrectly.
|
||||
// Dialogs, for example, live under a different Xaml root element than the rest of
|
||||
// our application. This makes our popup menus and buttons "disappear" when the
|
||||
// user wants Terminal to be in a different theme than the rest of the system.
|
||||
// This hack---and it _is_ a hack--walks up a dialog's ancestry and forces the
|
||||
// theme on each element up to the root. We're relying a bit on Xaml's implementation
|
||||
// details here, but it does have the desired effect.
|
||||
// It's not enough to set the theme on the dialog alone.
|
||||
auto themingLambda{ [this](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) {
|
||||
auto theme{ _settings->GlobalSettings().GetTheme() };
|
||||
auto element{ sender.try_as<winrt::Windows::UI::Xaml::FrameworkElement>() };
|
||||
while (element)
|
||||
{
|
||||
element.RequestedTheme(theme);
|
||||
element = element.Parent().try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
|
||||
}
|
||||
} };
|
||||
|
||||
themingLambda(dialog, nullptr); // if it's already in the tree
|
||||
auto loadedRevoker{ dialog.Loaded(winrt::auto_revoke, themingLambda) }; // if it's not yet in the tree
|
||||
|
||||
// Display the dialog.
|
||||
co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup);
|
||||
@@ -502,7 +545,7 @@ namespace winrt::TerminalApp::implementation
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings->GlobalSettings().GetRequestedTheme();
|
||||
return _settings->GlobalSettings().GetTheme();
|
||||
}
|
||||
|
||||
bool AppLogic::GetShowTabsInTitlebar()
|
||||
@@ -617,7 +660,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void AppLogic::_RegisterSettingsChange()
|
||||
{
|
||||
// Get the containing folder.
|
||||
std::filesystem::path settingsPath{ CascadiaSettings::GetSettingsPath() };
|
||||
const auto settingsPath{ CascadiaSettings::GetSettingsPath() };
|
||||
const auto folder = settingsPath.parent_path();
|
||||
|
||||
_reader.create(folder.c_str(),
|
||||
@@ -683,7 +726,7 @@ namespace winrt::TerminalApp::implementation
|
||||
co_await winrt::resume_foreground(_root->Dispatcher());
|
||||
|
||||
// Refresh the UI theme
|
||||
_ApplyTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
_ApplyTheme(_settings->GlobalSettings().GetTheme());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -842,6 +885,65 @@ namespace winrt::TerminalApp::implementation
|
||||
return { L"" };
|
||||
}
|
||||
|
||||
winrt::hstring AppLogic::ApplicationDisplayName() const
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
return package.DisplayName();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return RS_(L"ApplicationDisplayNameUnpackaged");
|
||||
}
|
||||
|
||||
winrt::hstring AppLogic::ApplicationVersion() const
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
const auto version{ package.Id().Version() };
|
||||
winrt::hstring formatted{ wil::str_printf<std::wstring>(L"%u.%u.%u.%u", version.Major, version.Minor, version.Build, version.Revision) };
|
||||
return formatted;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
// Try to get the version the old-fashioned way
|
||||
try
|
||||
{
|
||||
struct LocalizationInfo
|
||||
{
|
||||
WORD language, codepage;
|
||||
};
|
||||
// Use the current module instance handle for TerminalApp.dll, nullptr for WindowsTerminal.exe
|
||||
auto filename{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
|
||||
auto size{ GetFileVersionInfoSizeExW(0, filename.c_str(), nullptr) };
|
||||
THROW_LAST_ERROR_IF(size == 0);
|
||||
auto versionBuffer{ std::make_unique<std::byte[]>(size) };
|
||||
THROW_IF_WIN32_BOOL_FALSE(GetFileVersionInfoExW(0, filename.c_str(), 0, size, versionBuffer.get()));
|
||||
|
||||
// Get the list of Version localizations
|
||||
LocalizationInfo* pVarLocalization{ nullptr };
|
||||
UINT varLen{ 0 };
|
||||
THROW_IF_WIN32_BOOL_FALSE(VerQueryValueW(versionBuffer.get(), L"\\VarFileInfo\\Translation", reinterpret_cast<void**>(&pVarLocalization), &varLen));
|
||||
THROW_HR_IF(E_UNEXPECTED, varLen < sizeof(*pVarLocalization)); // there must be at least one translation
|
||||
|
||||
// Get the product version from the localized version compartment
|
||||
// We're using String/ProductVersion here because our build pipeline puts more rich information in it (like the branch name)
|
||||
// than in the unlocalized numeric version fields.
|
||||
WCHAR* pProductVersion{ nullptr };
|
||||
UINT versionLen{ 0 };
|
||||
const auto localizedVersionName{ wil::str_printf<std::wstring>(L"\\StringFileInfo\\%04x%04x\\ProductVersion",
|
||||
pVarLocalization->language ? pVarLocalization->language : 0x0409, // well-known en-US LCID
|
||||
pVarLocalization->codepage) };
|
||||
THROW_IF_WIN32_BOOL_FALSE(VerQueryValueW(versionBuffer.get(), localizedVersionName.c_str(), reinterpret_cast<void**>(&pProductVersion), &versionLen));
|
||||
return { pProductVersion };
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return RS_(L"ApplicationVersionUnknown");
|
||||
}
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// Winrt events need a method for adding a callback to the event and removing the callback.
|
||||
// These macros will define them both for you.
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace winrt::TerminalApp::implementation
|
||||
struct AppLogic : AppLogicT<AppLogic>
|
||||
{
|
||||
public:
|
||||
static AppLogic* Current() noexcept;
|
||||
|
||||
AppLogic();
|
||||
~AppLogic() = default;
|
||||
|
||||
@@ -28,6 +30,9 @@ namespace winrt::TerminalApp::implementation
|
||||
int32_t SetStartupCommandline(array_view<const winrt::hstring> actions);
|
||||
winrt::hstring EarlyExitMessage();
|
||||
|
||||
winrt::hstring ApplicationDisplayName() const;
|
||||
winrt::hstring ApplicationVersion() const;
|
||||
|
||||
Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi);
|
||||
winrt::Windows::Foundation::Point GetLaunchInitialPositions(int32_t defaultInitialX, int32_t defaultInitialY);
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme();
|
||||
|
||||
@@ -35,6 +35,9 @@ namespace TerminalApp
|
||||
|
||||
String Title { get; };
|
||||
|
||||
String ApplicationDisplayName { get; };
|
||||
String ApplicationVersion { get; };
|
||||
|
||||
Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi);
|
||||
Windows.Foundation.Point GetLaunchInitialPositions(Int32 defaultInitialX, Int32 defaultInitialY);
|
||||
Windows.UI.Xaml.ElementTheme GetRequestedTheme();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
#include "AppLogic.h"
|
||||
#include "Utils.h"
|
||||
#include "LibraryResources.h"
|
||||
|
||||
#include "PowershellCoreProfileGenerator.h"
|
||||
#include "WslDistroGenerator.h"
|
||||
@@ -27,18 +28,17 @@ static constexpr std::wstring_view PACKAGED_PROFILE_ICON_PATH{ L"ms-appx:///Prof
|
||||
static constexpr std::wstring_view PACKAGED_PROFILE_ICON_EXTENSION{ L".png" };
|
||||
static constexpr std::wstring_view DEFAULT_LINUX_ICON_GUID{ L"{9acb9455-ca41-5af7-950f-6bca1bc9722f}" };
|
||||
|
||||
// make sure this matches defaults.json.
|
||||
static constexpr std::wstring_view DEFAULT_WINDOWS_POWERSHELL_GUID{ L"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" };
|
||||
|
||||
// Method Description:
|
||||
// - Returns the settings currently in use by the entire Terminal application.
|
||||
// Throws:
|
||||
// - HR E_INVALIDARG if the app isn't up and running.
|
||||
const CascadiaSettings& CascadiaSettings::GetCurrentAppSettings()
|
||||
{
|
||||
auto currentXamlApp{ winrt::Windows::UI::Xaml::Application::Current().as<winrt::TerminalApp::App>() };
|
||||
THROW_HR_IF_NULL(E_INVALIDARG, currentXamlApp);
|
||||
|
||||
auto appLogic = winrt::get_self<winrt::TerminalApp::implementation::AppLogic>(currentXamlApp.Logic());
|
||||
auto appLogic{ ::winrt::TerminalApp::implementation::AppLogic::Current() };
|
||||
THROW_HR_IF_NULL(E_INVALIDARG, appLogic);
|
||||
|
||||
return *(appLogic->GetSettings());
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ CascadiaSettings::CascadiaSettings(const bool addDynamicProfiles)
|
||||
// - profileName: the name of the profile's GUID to return.
|
||||
// Return Value:
|
||||
// - the GUID associated with the profile name.
|
||||
std::optional<GUID> CascadiaSettings::FindGuid(const std::wstring& profileName) const noexcept
|
||||
std::optional<GUID> CascadiaSettings::FindGuid(const std::wstring_view profileName) const noexcept
|
||||
{
|
||||
std::optional<GUID> profileGuid{};
|
||||
|
||||
@@ -244,7 +244,7 @@ void CascadiaSettings::_ValidateProfilesHaveGuid()
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Checks if the "globals.defaultProfile" is set to one of the profiles we
|
||||
// - Checks if the "defaultProfile" is set to one of the profiles we
|
||||
// actually have. If the value is unset, or the value is set to something that
|
||||
// doesn't exist in the list of profiles, we'll arbitrarily pick the first
|
||||
// profile to use temporarily as the default.
|
||||
@@ -673,3 +673,40 @@ void CascadiaSettings::_ValidateKeybindings()
|
||||
_warnings.insert(_warnings.end(), keybindingWarnings.begin(), keybindingWarnings.end());
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description
|
||||
// - Replaces known tokens DEFAULT_PROFILE, PRODUCT and VERSION in the settings template
|
||||
// with their expected values. DEFAULT_PROFILE is updated to match PowerShell Core's GUID
|
||||
// if such a profile is detected. If it isn't, it'll be set to Windows PowerShell's GUID.
|
||||
// Arguments:
|
||||
// - settingsTemplate: a settings template
|
||||
// Return value:
|
||||
// - The new settings string.
|
||||
std::string CascadiaSettings::_ApplyFirstRunChangesToSettingsTemplate(std::string_view settingsTemplate) const
|
||||
{
|
||||
std::string finalSettings{ settingsTemplate };
|
||||
auto replace{ [](std::string& haystack, std::string_view needle, std::string_view replacement) {
|
||||
auto pos{ std::string::npos };
|
||||
while ((pos = haystack.rfind(needle, pos)) != std::string::npos)
|
||||
{
|
||||
haystack.replace(pos, needle.size(), replacement);
|
||||
}
|
||||
} };
|
||||
|
||||
std::wstring defaultProfileGuid{ DEFAULT_WINDOWS_POWERSHELL_GUID };
|
||||
if (const auto psCoreProfileGuid{ FindGuid(PowershellCoreProfileGenerator::GetPreferredPowershellProfileName()) })
|
||||
{
|
||||
defaultProfileGuid = Utils::GuidToString(*psCoreProfileGuid);
|
||||
}
|
||||
|
||||
replace(finalSettings, "%DEFAULT_PROFILE%", til::u16u8(defaultProfileGuid));
|
||||
if (const auto appLogic{ winrt::TerminalApp::implementation::AppLogic::Current() })
|
||||
{
|
||||
replace(finalSettings, "%VERSION%", til::u16u8(appLogic->ApplicationVersion()));
|
||||
replace(finalSettings, "%PRODUCT%", til::u16u8(appLogic->ApplicationDisplayName()));
|
||||
}
|
||||
|
||||
replace(finalSettings, "%COMMAND_PROMPT_LOCALIZED_NAME%", RS_A(L"CommandPromptDisplayName"));
|
||||
|
||||
return finalSettings;
|
||||
}
|
||||
|
||||
@@ -66,10 +66,10 @@ public:
|
||||
static std::unique_ptr<CascadiaSettings> FromJson(const Json::Value& json);
|
||||
void LayerJson(const Json::Value& json);
|
||||
|
||||
static std::wstring GetSettingsPath(const bool useRoamingPath = false);
|
||||
static std::wstring GetDefaultSettingsPath();
|
||||
static std::filesystem::path GetSettingsPath();
|
||||
static std::filesystem::path GetDefaultSettingsPath();
|
||||
|
||||
std::optional<GUID> FindGuid(const std::wstring& profileName) const noexcept;
|
||||
std::optional<GUID> FindGuid(const std::wstring_view profileName) const noexcept;
|
||||
const Profile* FindProfile(GUID profileGuid) const noexcept;
|
||||
|
||||
std::vector<TerminalApp::SettingsLoadWarnings>& GetWarnings();
|
||||
@@ -95,6 +95,7 @@ private:
|
||||
static const Json::Value& _GetDisabledProfileSourcesJsonObject(const Json::Value& json);
|
||||
bool _PrependSchemaDirective();
|
||||
bool _AppendDynamicProfilesToUserSettings();
|
||||
std::string _ApplyFirstRunChangesToSettingsTemplate(std::string_view settingsTemplate) const;
|
||||
|
||||
void _ApplyDefaultsFromUserSettings();
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// defaults.h is a file containing the default json settings in a std::string_view
|
||||
#include "defaults.h"
|
||||
#include "defaults-universal.h"
|
||||
// userDefault.h is like the above, but with a default template for the user's profiles.json.
|
||||
// userDefault.h is like the above, but with a default template for the user's settings.json.
|
||||
#include "userDefaults.h"
|
||||
// Both defaults.h and userDefaults.h are generated at build time into the
|
||||
// "Generated Files" directory.
|
||||
@@ -23,7 +23,8 @@ using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace ::Microsoft::Console;
|
||||
|
||||
static constexpr std::wstring_view SettingsFilename{ L"profiles.json" };
|
||||
static constexpr std::wstring_view SettingsFilename{ L"settings.json" };
|
||||
static constexpr std::wstring_view LegacySettingsFilename{ L"profiles.json" };
|
||||
static constexpr std::wstring_view UnpackagedSettingsFolderName{ L"Microsoft\\Windows Terminal\\" };
|
||||
|
||||
static constexpr std::wstring_view DefaultsFilename{ L"defaults.json" };
|
||||
@@ -33,7 +34,6 @@ static constexpr std::string_view ProfilesKey{ "profiles" };
|
||||
static constexpr std::string_view DefaultSettingsKey{ "defaults" };
|
||||
static constexpr std::string_view ProfilesListKey{ "list" };
|
||||
static constexpr std::string_view KeybindingsKey{ "keybindings" };
|
||||
static constexpr std::string_view GlobalsKey{ "globals" };
|
||||
static constexpr std::string_view SchemesKey{ "schemes" };
|
||||
|
||||
static constexpr std::string_view DisabledProfileSourcesKey{ "disabledProfileSources" };
|
||||
@@ -48,7 +48,7 @@ static constexpr std::string_view SettingsSchemaFragment{ "\n"
|
||||
// it will load the settings from our packaged localappdata. If we're
|
||||
// running as an unpackaged application, it will read it from the path
|
||||
// we've set under localappdata.
|
||||
// - Loads both the settings from the defaults.json and the user's profiles.json
|
||||
// - Loads both the settings from the defaults.json and the user's settings.json
|
||||
// - Also runs and dynamic profile generators. If any of those generators create
|
||||
// new profiles, we'll write the user settings back to the file, with the new
|
||||
// profiles inserted into their list of profiles.
|
||||
@@ -74,20 +74,22 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
|
||||
{
|
||||
resultPtr->_ParseJsonString(fileData.value(), false);
|
||||
}
|
||||
else
|
||||
|
||||
// Load profiles from dynamic profile generators. _userSettings should be
|
||||
// created by now, because we're going to check in there for any generators
|
||||
// that should be disabled (if the user had any settings.)
|
||||
resultPtr->_LoadDynamicProfiles();
|
||||
|
||||
if (!fileHasData)
|
||||
{
|
||||
// We didn't find the user settings. We'll need to create a file
|
||||
// to use as the user defaults.
|
||||
// For now, just parse our user settings template as their user settings.
|
||||
resultPtr->_ParseJsonString(UserSettingsJson, false);
|
||||
auto userSettings{ resultPtr->_ApplyFirstRunChangesToSettingsTemplate(UserSettingsJson) };
|
||||
resultPtr->_ParseJsonString(userSettings, false);
|
||||
needToWriteFile = true;
|
||||
}
|
||||
|
||||
// Load profiles from dynamic profile generators. _userSettings should be
|
||||
// created by now, because we're going to check in there for any generators
|
||||
// that should be disabled.
|
||||
resultPtr->_LoadDynamicProfiles();
|
||||
|
||||
// See microsoft/terminal#2325: find the defaultSettings from the user's
|
||||
// settings. Layer those settings upon all the existing profiles we have
|
||||
// (defaults and dynamic profiles). We'll also set
|
||||
@@ -501,19 +503,8 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::FromJson(const Json::Value&
|
||||
// <none>
|
||||
void CascadiaSettings::LayerJson(const Json::Value& json)
|
||||
{
|
||||
// microsoft/terminal#2906: First layer the root object as our globals. If
|
||||
// there is also a `globals` object, layer that one on top of the settings
|
||||
// from the root.
|
||||
_globals.LayerJson(json);
|
||||
|
||||
if (auto globals{ json[GlobalsKey.data()] })
|
||||
{
|
||||
if (globals.isObject())
|
||||
{
|
||||
_globals.LayerJson(globals);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto schemes{ json[SchemesKey.data()] })
|
||||
{
|
||||
for (auto schemeJson : schemes)
|
||||
@@ -619,7 +610,6 @@ void CascadiaSettings::_ApplyDefaultsFromUserSettings()
|
||||
// If `profiles` was an object, then look for the `defaults` object
|
||||
// underneath it for the default profile settings.
|
||||
auto defaultSettings{ Json::Value::null };
|
||||
|
||||
if (const auto profiles{ _userSettings[JsonKey(ProfilesKey)] })
|
||||
{
|
||||
if (profiles.isObject())
|
||||
@@ -628,6 +618,8 @@ void CascadiaSettings::_ApplyDefaultsFromUserSettings()
|
||||
}
|
||||
}
|
||||
|
||||
// cache and apply default profile settings
|
||||
// from user settings file
|
||||
if (defaultSettings)
|
||||
{
|
||||
_userDefaultProfileSettings = defaultSettings;
|
||||
@@ -757,33 +749,31 @@ std::optional<std::string> CascadiaSettings::_ReadUserSettings()
|
||||
|
||||
if (!hFile)
|
||||
{
|
||||
// GH#1770 - Now that we're _not_ roaming our settings, do a quick check
|
||||
// to see if there's a file in the Roaming App data folder. If there is
|
||||
// a file there, but not in the LocalAppData, it's likely the user is
|
||||
// upgrading from a version of the terminal from before this change.
|
||||
// We'll try moving the file from the Roaming app data folder to the
|
||||
// local appdata folder.
|
||||
// GH#5186 - We moved from profiles.json to settings.json; we want to
|
||||
// migrate any file we find. We're using MoveFile in case their settings.json
|
||||
// is a symbolic link.
|
||||
auto pathToLegacySettingsFile{ pathToSettingsFile };
|
||||
pathToLegacySettingsFile.replace_filename(LegacySettingsFilename);
|
||||
|
||||
const auto pathToRoamingSettingsFile{ CascadiaSettings::GetSettingsPath(true) };
|
||||
wil::unique_hfile hRoamingFile{ CreateFileW(pathToRoamingSettingsFile.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr) };
|
||||
wil::unique_hfile hLegacyFile{ CreateFileW(pathToLegacySettingsFile.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr) };
|
||||
|
||||
if (hRoamingFile)
|
||||
if (hLegacyFile)
|
||||
{
|
||||
// Close the file handle, move it, and re-open the file in its new location.
|
||||
hRoamingFile.reset();
|
||||
hLegacyFile.reset();
|
||||
|
||||
// Note: We're unsure if this is unsafe. Theoretically it's possible
|
||||
// that two instances of the app will try and move the settings file
|
||||
// simultaneously. We don't know what might happen in that scenario,
|
||||
// but we're also not sure how to safely lock the file to prevent
|
||||
// that from occurring.
|
||||
THROW_LAST_ERROR_IF(!MoveFile(pathToRoamingSettingsFile.c_str(),
|
||||
THROW_LAST_ERROR_IF(!MoveFile(pathToLegacySettingsFile.c_str(),
|
||||
pathToSettingsFile.c_str()));
|
||||
|
||||
hFile.reset(CreateFileW(pathToSettingsFile.c_str(),
|
||||
@@ -841,22 +831,18 @@ std::optional<std::string> CascadiaSettings::_ReadFile(HANDLE hFile)
|
||||
// package, or in its unpackaged location. This path is under the "Local
|
||||
// AppData" folder, so it _doesn't_ roam to other machines.
|
||||
// - If the application is unpackaged,
|
||||
// the file will end up under e.g. C:\Users\admin\AppData\Local\Microsoft\Windows Terminal\profiles.json
|
||||
// the file will end up under e.g. C:\Users\admin\AppData\Local\Microsoft\Windows Terminal\settings.json
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the full path to the settings file
|
||||
std::wstring CascadiaSettings::GetSettingsPath(const bool useRoamingPath)
|
||||
std::filesystem::path CascadiaSettings::GetSettingsPath()
|
||||
{
|
||||
wil::unique_cotaskmem_string localAppDataFolder;
|
||||
// KF_FLAG_FORCE_APP_DATA_REDIRECTION, when engaged, causes SHGet... to return
|
||||
// the new AppModel paths (Packages/xxx/RoamingState, etc.) for standard path requests.
|
||||
// Using this flag allows us to avoid Windows.Storage.ApplicationData completely.
|
||||
const auto knowFolderId = useRoamingPath ? FOLDERID_RoamingAppData : FOLDERID_LocalAppData;
|
||||
if (FAILED(SHGetKnownFolderPath(knowFolderId, KF_FLAG_FORCE_APP_DATA_REDIRECTION, nullptr, &localAppDataFolder)))
|
||||
{
|
||||
THROW_LAST_ERROR();
|
||||
}
|
||||
THROW_IF_FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_FORCE_APP_DATA_REDIRECTION, nullptr, &localAppDataFolder));
|
||||
|
||||
std::filesystem::path parentDirectoryForSettingsFile{ localAppDataFolder.get() };
|
||||
|
||||
@@ -871,7 +857,7 @@ std::wstring CascadiaSettings::GetSettingsPath(const bool useRoamingPath)
|
||||
return parentDirectoryForSettingsFile / SettingsFilename;
|
||||
}
|
||||
|
||||
std::wstring CascadiaSettings::GetDefaultSettingsPath()
|
||||
std::filesystem::path CascadiaSettings::GetDefaultSettingsPath()
|
||||
{
|
||||
// Both of these posts suggest getting the path to the exe, then removing
|
||||
// the exe's name to get the package root:
|
||||
@@ -919,10 +905,9 @@ const Json::Value& CascadiaSettings::_GetProfilesJsonObject(const Json::Value& j
|
||||
// given object
|
||||
const Json::Value& CascadiaSettings::_GetDisabledProfileSourcesJsonObject(const Json::Value& json)
|
||||
{
|
||||
// Check the globals first, then look in the root.
|
||||
if (json.isMember(JsonKey(GlobalsKey)))
|
||||
if (!json)
|
||||
{
|
||||
return json[JsonKey(GlobalsKey)][JsonKey(DisabledProfileSourcesKey)];
|
||||
return Json::Value::nullSingleton();
|
||||
}
|
||||
return json[JsonKey(DisabledProfileSourcesKey)];
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
|
||||
static constexpr std::string_view NameKey{ "name" };
|
||||
static constexpr std::string_view TableKey{ "colors" };
|
||||
static constexpr std::string_view ForegroundKey{ "foreground" };
|
||||
static constexpr std::string_view BackgroundKey{ "background" };
|
||||
static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" };
|
||||
@@ -177,22 +176,6 @@ void ColorScheme::LayerJson(const Json::Value& json)
|
||||
_cursorColor = color;
|
||||
}
|
||||
|
||||
// Legacy Deserialization. Leave in place to allow forward compatibility
|
||||
if (auto table{ json[JsonKey(TableKey)] })
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (const auto& tableEntry : table)
|
||||
{
|
||||
if (tableEntry.isString())
|
||||
{
|
||||
auto color = Utils::ColorFromHexString(tableEntry.asString());
|
||||
_table.at(i) = color;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (const auto& current : TableColors)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ TerminalApp::Profile CreateDefaultProfile(const std::wstring_view name)
|
||||
gsl::as_bytes(gsl::make_span(name))) };
|
||||
TerminalApp::Profile newProfile{ profileGuid };
|
||||
|
||||
newProfile.SetName(static_cast<std::wstring>(name));
|
||||
newProfile.SetName(name);
|
||||
|
||||
std::wstring iconPath{ PACKAGED_PROFILE_ICON_PATH };
|
||||
iconPath.append(Microsoft::Console::Utils::GuidToString(profileGuid));
|
||||
|
||||
@@ -25,13 +25,14 @@ static constexpr std::string_view InitialColsKey{ "initialCols" };
|
||||
static constexpr std::string_view RowsToScrollKey{ "rowsToScroll" };
|
||||
static constexpr std::string_view InitialPositionKey{ "initialPosition" };
|
||||
static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" };
|
||||
static constexpr std::string_view RequestedThemeKey{ "requestedTheme" };
|
||||
static constexpr std::string_view ThemeKey{ "theme" };
|
||||
static constexpr std::string_view TabWidthModeKey{ "tabWidthMode" };
|
||||
static constexpr std::wstring_view EqualTabWidthModeValue{ L"equal" };
|
||||
static constexpr std::wstring_view TitleLengthTabWidthModeValue{ L"titleLength" };
|
||||
static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" };
|
||||
static constexpr std::string_view WordDelimitersKey{ "wordDelimiters" };
|
||||
static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" };
|
||||
static constexpr std::string_view CopyFormattingKey{ "copyFormatting" };
|
||||
static constexpr std::string_view LaunchModeKey{ "launchMode" };
|
||||
static constexpr std::string_view ConfirmCloseAllKey{ "confirmCloseAllTabs" };
|
||||
static constexpr std::string_view SnapToGridOnResizeKey{ "snapToGridOnResize" };
|
||||
@@ -63,10 +64,11 @@ GlobalAppSettings::GlobalAppSettings() :
|
||||
_initialY{},
|
||||
_showTitleInTitlebar{ true },
|
||||
_showTabsInTitlebar{ true },
|
||||
_requestedTheme{ ElementTheme::Default },
|
||||
_theme{ ElementTheme::Default },
|
||||
_tabWidthMode{ TabViewWidthMode::Equal },
|
||||
_wordDelimiters{ DEFAULT_WORD_DELIMITERS },
|
||||
_copyOnSelect{ false },
|
||||
_copyFormatting{ false },
|
||||
_launchMode{ LaunchMode::DefaultMode },
|
||||
_debugFeatures{ debugFeaturesDefault }
|
||||
{
|
||||
@@ -121,14 +123,14 @@ void GlobalAppSettings::SetShowTitleInTitlebar(const bool showTitleInTitlebar) n
|
||||
_showTitleInTitlebar = showTitleInTitlebar;
|
||||
}
|
||||
|
||||
ElementTheme GlobalAppSettings::GetRequestedTheme() const noexcept
|
||||
ElementTheme GlobalAppSettings::GetTheme() const noexcept
|
||||
{
|
||||
return _requestedTheme;
|
||||
return _theme;
|
||||
}
|
||||
|
||||
void GlobalAppSettings::SetRequestedTheme(const ElementTheme requestedTheme) noexcept
|
||||
void GlobalAppSettings::SetTheme(const ElementTheme theme) noexcept
|
||||
{
|
||||
_requestedTheme = requestedTheme;
|
||||
_theme = theme;
|
||||
}
|
||||
|
||||
TabViewWidthMode GlobalAppSettings::GetTabWidthMode() const noexcept
|
||||
@@ -161,6 +163,11 @@ void GlobalAppSettings::SetCopyOnSelect(const bool copyOnSelect) noexcept
|
||||
_copyOnSelect = copyOnSelect;
|
||||
}
|
||||
|
||||
bool GlobalAppSettings::GetCopyFormatting() const noexcept
|
||||
{
|
||||
return _copyFormatting;
|
||||
}
|
||||
|
||||
LaunchMode GlobalAppSettings::GetLaunchMode() const noexcept
|
||||
{
|
||||
return _launchMode;
|
||||
@@ -245,8 +252,9 @@ Json::Value GlobalAppSettings::ToJson() const
|
||||
jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar;
|
||||
jsonObject[JsonKey(WordDelimitersKey)] = winrt::to_string(_wordDelimiters);
|
||||
jsonObject[JsonKey(CopyOnSelectKey)] = _copyOnSelect;
|
||||
jsonObject[JsonKey(CopyFormattingKey)] = _copyFormatting;
|
||||
jsonObject[JsonKey(LaunchModeKey)] = winrt::to_string(_SerializeLaunchMode(_launchMode));
|
||||
jsonObject[JsonKey(RequestedThemeKey)] = winrt::to_string(_SerializeTheme(_requestedTheme));
|
||||
jsonObject[JsonKey(ThemeKey)] = winrt::to_string(_SerializeTheme(_theme));
|
||||
jsonObject[JsonKey(TabWidthModeKey)] = winrt::to_string(_SerializeTabWidthMode(_tabWidthMode));
|
||||
jsonObject[JsonKey(KeybindingsKey)] = _keybindings->ToJson();
|
||||
jsonObject[JsonKey(ConfirmCloseAllKey)] = _confirmCloseAllTabs;
|
||||
@@ -311,14 +319,16 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
|
||||
JsonUtils::GetBool(json, CopyOnSelectKey, _copyOnSelect);
|
||||
|
||||
JsonUtils::GetBool(json, CopyFormattingKey, _copyFormatting);
|
||||
|
||||
if (auto launchMode{ json[JsonKey(LaunchModeKey)] })
|
||||
{
|
||||
_launchMode = _ParseLaunchMode(GetWstringFromJson(launchMode));
|
||||
}
|
||||
|
||||
if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] })
|
||||
if (auto theme{ json[JsonKey(ThemeKey)] })
|
||||
{
|
||||
_requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme));
|
||||
_theme = _ParseTheme(GetWstringFromJson(theme));
|
||||
}
|
||||
|
||||
if (auto tabWidthMode{ json[JsonKey(TabWidthModeKey)] })
|
||||
|
||||
@@ -53,8 +53,10 @@ public:
|
||||
bool GetConfirmCloseAllTabs() const noexcept;
|
||||
void SetConfirmCloseAllTabs(const bool confirmCloseAllTabs) noexcept;
|
||||
|
||||
void SetRequestedTheme(const winrt::Windows::UI::Xaml::ElementTheme requestedTheme) noexcept;
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetTheme() const noexcept;
|
||||
void SetTheme(const winrt::Windows::UI::Xaml::ElementTheme requestedTheme) noexcept;
|
||||
|
||||
winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode GetTabWidthMode() const noexcept;
|
||||
void SetTabWidthMode(const winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode tabWidthMode);
|
||||
|
||||
bool GetShowTabsInTitlebar() const noexcept;
|
||||
@@ -66,6 +68,8 @@ public:
|
||||
bool GetCopyOnSelect() const noexcept;
|
||||
void SetCopyOnSelect(const bool copyOnSelect) noexcept;
|
||||
|
||||
bool GetCopyFormatting() const noexcept;
|
||||
|
||||
std::optional<int32_t> GetInitialX() const noexcept;
|
||||
|
||||
std::optional<int32_t> GetInitialY() const noexcept;
|
||||
@@ -73,10 +77,6 @@ public:
|
||||
winrt::TerminalApp::LaunchMode GetLaunchMode() const noexcept;
|
||||
void SetLaunchMode(const winrt::TerminalApp::LaunchMode launchMode);
|
||||
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept;
|
||||
|
||||
winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode GetTabWidthMode() const noexcept;
|
||||
|
||||
bool DebugFeaturesEnabled() const noexcept;
|
||||
|
||||
Json::Value ToJson() const;
|
||||
@@ -112,7 +112,8 @@ private:
|
||||
bool _showTabsInTitlebar;
|
||||
std::wstring _wordDelimiters;
|
||||
bool _copyOnSelect;
|
||||
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
|
||||
bool _copyFormatting;
|
||||
winrt::Windows::UI::Xaml::ElementTheme _theme;
|
||||
winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode _tabWidthMode;
|
||||
|
||||
winrt::TerminalApp::LaunchMode _launchMode;
|
||||
|
||||
@@ -21,6 +21,7 @@ static constexpr std::wstring_view POWERSHELL_PREVIEW_PFN{ L"Microsoft.PowerShel
|
||||
static constexpr std::wstring_view PWSH_EXE{ L"pwsh.exe" };
|
||||
static constexpr std::wstring_view POWERSHELL_ICON{ L"ms-appx:///ProfileIcons/pwsh.png" };
|
||||
static constexpr std::wstring_view POWERSHELL_PREVIEW_ICON{ L"ms-appx:///ProfileIcons/pwsh-preview.png" };
|
||||
static constexpr std::wstring_view POWERSHELL_PREFERRED_PROFILE_NAME{ L"PowerShell" };
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -322,8 +323,17 @@ std::vector<TerminalApp::Profile> PowershellCoreProfileGenerator::GenerateProfil
|
||||
// (or the closest approximation thereof). It may choose a preview instance as the "best" if it is a higher version.
|
||||
auto firstProfile = profiles.begin();
|
||||
firstProfile->SetGuid(PowershellCoreGuid);
|
||||
firstProfile->SetName(L"PowerShell");
|
||||
firstProfile->SetName(POWERSHELL_PREFERRED_PROFILE_NAME);
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Returns the thing it's named for.
|
||||
// Return value:
|
||||
// - the thing it says in the name
|
||||
const std::wstring_view PowershellCoreProfileGenerator::GetPreferredPowershellProfileName()
|
||||
{
|
||||
return POWERSHELL_PREFERRED_PROFILE_NAME;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace TerminalApp
|
||||
class PowershellCoreProfileGenerator : public TerminalApp::IDynamicProfileGenerator
|
||||
{
|
||||
public:
|
||||
static const std::wstring_view GetPreferredPowershellProfileName();
|
||||
|
||||
PowershellCoreProfileGenerator() = default;
|
||||
~PowershellCoreProfileGenerator() = default;
|
||||
std::wstring_view GetNamespace() override;
|
||||
|
||||
@@ -19,13 +19,11 @@ static constexpr std::string_view NameKey{ "name" };
|
||||
static constexpr std::string_view GuidKey{ "guid" };
|
||||
static constexpr std::string_view SourceKey{ "source" };
|
||||
static constexpr std::string_view ColorSchemeKey{ "colorScheme" };
|
||||
static constexpr std::string_view ColorSchemeKeyOld{ "colorscheme" };
|
||||
static constexpr std::string_view HiddenKey{ "hidden" };
|
||||
|
||||
static constexpr std::string_view ForegroundKey{ "foreground" };
|
||||
static constexpr std::string_view BackgroundKey{ "background" };
|
||||
static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" };
|
||||
static constexpr std::string_view ColorTableKey{ "colorTable" };
|
||||
static constexpr std::string_view TabTitleKey{ "tabTitle" };
|
||||
static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" };
|
||||
static constexpr std::string_view HistorySizeKey{ "historySize" };
|
||||
@@ -105,7 +103,6 @@ Profile::Profile(const std::optional<GUID>& guid) :
|
||||
_defaultBackground{},
|
||||
_selectionBackground{},
|
||||
_cursorColor{},
|
||||
_colorTable{},
|
||||
_tabTitle{},
|
||||
_suppressApplicationTitle{},
|
||||
_historySize{ DEFAULT_HISTORY_SIZE },
|
||||
@@ -171,11 +168,6 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
|
||||
TerminalSettings terminalSettings{};
|
||||
|
||||
// Fill in the Terminal Setting's CoreSettings from the profile
|
||||
auto const colorTableCount = gsl::narrow_cast<int>(_colorTable.size());
|
||||
for (int i = 0; i < colorTableCount; i++)
|
||||
{
|
||||
terminalSettings.SetColorTableEntry(i, _colorTable[i]);
|
||||
}
|
||||
terminalSettings.HistorySize(_historySize);
|
||||
terminalSettings.SnapOnInput(_snapOnInput);
|
||||
terminalSettings.CursorHeight(_cursorHeight);
|
||||
@@ -308,15 +300,6 @@ Json::Value Profile::ToJson() const
|
||||
const auto scheme = winrt::to_string(_schemeName.value());
|
||||
root[JsonKey(ColorSchemeKey)] = scheme;
|
||||
}
|
||||
else
|
||||
{
|
||||
Json::Value tableArray{};
|
||||
for (auto& color : _colorTable)
|
||||
{
|
||||
tableArray.append(Utils::ColorToHexString(color));
|
||||
}
|
||||
root[JsonKey(ColorTableKey)] = tableArray;
|
||||
}
|
||||
root[JsonKey(HistorySizeKey)] = _historySize;
|
||||
root[JsonKey(SnapOnInputKey)] = _snapOnInput;
|
||||
// Only add the cursor height property if we're a legacy-style cursor.
|
||||
@@ -644,26 +627,6 @@ void Profile::LayerJson(const Json::Value& json)
|
||||
JsonUtils::GetOptionalColor(json, CursorColorKey, _cursorColor);
|
||||
|
||||
JsonUtils::GetOptionalString(json, ColorSchemeKey, _schemeName);
|
||||
// TODO:GH#1069 deprecate old settings key
|
||||
JsonUtils::GetOptionalString(json, ColorSchemeKeyOld, _schemeName);
|
||||
|
||||
// Only look for the "table" if there's no "schemeName"
|
||||
if (!(json.isMember(JsonKey(ColorSchemeKey))) &&
|
||||
!(json.isMember(JsonKey(ColorSchemeKeyOld))) &&
|
||||
json.isMember(JsonKey(ColorTableKey)))
|
||||
{
|
||||
auto colortable{ json[JsonKey(ColorTableKey)] };
|
||||
int i = 0;
|
||||
for (const auto& tableEntry : colortable)
|
||||
{
|
||||
if (tableEntry.isString())
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(tableEntry.asString());
|
||||
_colorTable[i] = color;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback"
|
||||
JsonUtils::GetInt(json, HistorySizeKey, _historySize);
|
||||
@@ -755,9 +718,9 @@ void Profile::SetStartingDirectory(std::wstring startingDirectory) noexcept
|
||||
_startingDirectory = std::move(startingDirectory);
|
||||
}
|
||||
|
||||
void Profile::SetName(std::wstring name) noexcept
|
||||
void Profile::SetName(const std::wstring_view name) noexcept
|
||||
{
|
||||
_name = std::move(name);
|
||||
_name = static_cast<std::wstring>(name);
|
||||
}
|
||||
|
||||
void Profile::SetUseAcrylic(bool useAcrylic) noexcept
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
void SetAcrylicOpacity(double opacity) noexcept;
|
||||
void SetCommandline(std::wstring cmdline) noexcept;
|
||||
void SetStartingDirectory(std::wstring startingDirectory) noexcept;
|
||||
void SetName(std::wstring name) noexcept;
|
||||
void SetName(const std::wstring_view name) noexcept;
|
||||
void SetUseAcrylic(bool useAcrylic) noexcept;
|
||||
void SetDefaultForeground(COLORREF defaultForeground) noexcept;
|
||||
void SetDefaultBackground(COLORREF defaultBackground) noexcept;
|
||||
@@ -143,7 +143,6 @@ private:
|
||||
std::optional<uint32_t> _defaultBackground;
|
||||
std::optional<uint32_t> _selectionBackground;
|
||||
std::optional<uint32_t> _cursorColor;
|
||||
std::array<uint32_t, COLOR_TABLE_SIZE> _colorTable;
|
||||
std::optional<std::wstring> _tabTitle;
|
||||
bool _suppressApplicationTitle;
|
||||
int32_t _historySize;
|
||||
|
||||
@@ -163,7 +163,7 @@ Temporarily using the Windows Terminal default settings.
|
||||
<value>Failed to reload settings</value>
|
||||
</data>
|
||||
<data name="FeedbackUriValue" xml:space="preserve">
|
||||
<value>https://aka.ms/terminal-feedback</value>
|
||||
<value>https://go.microsoft.com/fwlink/?linkid=2125419</value>
|
||||
</data>
|
||||
<data name="AboutMenuItem" xml:space="preserve">
|
||||
<value>About</value>
|
||||
@@ -266,11 +266,11 @@ Temporarily using the Windows Terminal default settings.
|
||||
<value>Privacy Policy</value>
|
||||
<comment>A hyperlink name for the Terminal's privacy policy</comment>
|
||||
</data>
|
||||
<data name="AboutDialog_DisplayNameUnpackaged" xml:space="preserve">
|
||||
<data name="ApplicationDisplayNameUnpackaged" xml:space="preserve">
|
||||
<value>Windows Terminal (Unpackaged)</value>
|
||||
<comment>This display name is used when the application's name cannot be determined</comment>
|
||||
</data>
|
||||
<data name="AboutDialog_VersionUnknown" xml:space="preserve">
|
||||
<data name="ApplicationVersionUnknown" xml:space="preserve">
|
||||
<value>Unknown</value>
|
||||
<comment>This is displayed when the version of the application cannot be determined</comment>
|
||||
</data>
|
||||
@@ -283,4 +283,8 @@ Temporarily using the Windows Terminal default settings.
|
||||
<data name="CloseAllDialog.Title" xml:space="preserve">
|
||||
<value>Do you want to close all tabs?</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="CommandPromptDisplayName" xml:space="preserve">
|
||||
<value>Command Prompt</value>
|
||||
<comment>This is the name of "Command Prompt", as localized in Windows. The localization here should match the one in the Windows product for "Command Prompt"</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -33,11 +33,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_CopyTextHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::CopyTextWithoutNewlines:
|
||||
{
|
||||
_CopyTextHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::PasteText:
|
||||
{
|
||||
_PasteTextHandlers(*this, *eventArgs);
|
||||
@@ -60,15 +55,6 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
case ShortcutAction::NewTab:
|
||||
case ShortcutAction::NewTabProfile0:
|
||||
case ShortcutAction::NewTabProfile1:
|
||||
case ShortcutAction::NewTabProfile2:
|
||||
case ShortcutAction::NewTabProfile3:
|
||||
case ShortcutAction::NewTabProfile4:
|
||||
case ShortcutAction::NewTabProfile5:
|
||||
case ShortcutAction::NewTabProfile6:
|
||||
case ShortcutAction::NewTabProfile7:
|
||||
case ShortcutAction::NewTabProfile8:
|
||||
{
|
||||
_NewTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
@@ -136,46 +122,24 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
case ShortcutAction::SwitchToTab:
|
||||
case ShortcutAction::SwitchToTab0:
|
||||
case ShortcutAction::SwitchToTab1:
|
||||
case ShortcutAction::SwitchToTab2:
|
||||
case ShortcutAction::SwitchToTab3:
|
||||
case ShortcutAction::SwitchToTab4:
|
||||
case ShortcutAction::SwitchToTab5:
|
||||
case ShortcutAction::SwitchToTab6:
|
||||
case ShortcutAction::SwitchToTab7:
|
||||
case ShortcutAction::SwitchToTab8:
|
||||
{
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::ResizePane:
|
||||
case ShortcutAction::ResizePaneLeft:
|
||||
case ShortcutAction::ResizePaneRight:
|
||||
case ShortcutAction::ResizePaneUp:
|
||||
case ShortcutAction::ResizePaneDown:
|
||||
{
|
||||
_ResizePaneHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::MoveFocus:
|
||||
case ShortcutAction::MoveFocusLeft:
|
||||
case ShortcutAction::MoveFocusRight:
|
||||
case ShortcutAction::MoveFocusUp:
|
||||
case ShortcutAction::MoveFocusDown:
|
||||
{
|
||||
_MoveFocusHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::IncreaseFontSize:
|
||||
{
|
||||
_AdjustFontSizeHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::DecreaseFontSize:
|
||||
case ShortcutAction::AdjustFontSize:
|
||||
{
|
||||
_AdjustFontSizeHandlers(*this, *eventArgs);
|
||||
break;
|
||||
|
||||
@@ -11,20 +11,10 @@ namespace TerminalApp
|
||||
{
|
||||
Invalid = 0,
|
||||
CopyText,
|
||||
CopyTextWithoutNewlines,
|
||||
PasteText,
|
||||
OpenNewTabDropdown,
|
||||
DuplicateTab,
|
||||
NewTab,
|
||||
NewTabProfile0, // Legacy
|
||||
NewTabProfile1, // Legacy
|
||||
NewTabProfile2, // Legacy
|
||||
NewTabProfile3, // Legacy
|
||||
NewTabProfile4, // Legacy
|
||||
NewTabProfile5, // Legacy
|
||||
NewTabProfile6, // Legacy
|
||||
NewTabProfile7, // Legacy
|
||||
NewTabProfile8, // Legacy
|
||||
NewWindow,
|
||||
CloseWindow,
|
||||
CloseTab,
|
||||
@@ -35,32 +25,14 @@ namespace TerminalApp
|
||||
SplitHorizontal,
|
||||
SplitPane,
|
||||
SwitchToTab,
|
||||
SwitchToTab0, // Legacy
|
||||
SwitchToTab1, // Legacy
|
||||
SwitchToTab2, // Legacy
|
||||
SwitchToTab3, // Legacy
|
||||
SwitchToTab4, // Legacy
|
||||
SwitchToTab5, // Legacy
|
||||
SwitchToTab6, // Legacy
|
||||
SwitchToTab7, // Legacy
|
||||
SwitchToTab8, // Legacy
|
||||
IncreaseFontSize,
|
||||
DecreaseFontSize,
|
||||
AdjustFontSize,
|
||||
ResetFontSize,
|
||||
ScrollUp,
|
||||
ScrollDown,
|
||||
ScrollUpPage,
|
||||
ScrollDownPage,
|
||||
ResizePane,
|
||||
ResizePaneLeft, // Legacy
|
||||
ResizePaneRight, // Legacy
|
||||
ResizePaneUp, // Legacy
|
||||
ResizePaneDown, // Legacy
|
||||
MoveFocus,
|
||||
MoveFocusLeft, // Legacy
|
||||
MoveFocusRight, // Legacy
|
||||
MoveFocusUp, // Legacy
|
||||
MoveFocusDown, // Legacy
|
||||
Find,
|
||||
ToggleFullscreen,
|
||||
OpenSettings
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "TerminalPage.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "Utils.h"
|
||||
#include "AppLogic.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
@@ -234,28 +235,22 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
winrt::hstring TerminalPage::ApplicationDisplayName()
|
||||
{
|
||||
try
|
||||
if (const auto appLogic{ implementation::AppLogic::Current() })
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
return package.DisplayName();
|
||||
return appLogic->ApplicationDisplayName();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return RS_(L"AboutDialog_DisplayNameUnpackaged");
|
||||
return RS_(L"ApplicationDisplayNameUnpackaged");
|
||||
}
|
||||
|
||||
winrt::hstring TerminalPage::ApplicationVersion()
|
||||
{
|
||||
try
|
||||
if (const auto appLogic{ implementation::AppLogic::Current() })
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
const auto version{ package.Id().Version() };
|
||||
winrt::hstring formatted{ wil::str_printf<std::wstring>(L"%u.%u.%u.%u", version.Major, version.Minor, version.Build, version.Revision) };
|
||||
return formatted;
|
||||
return appLogic->ApplicationVersion();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return RS_(L"AboutDialog_VersionUnknown");
|
||||
return RS_(L"ApplicationVersionUnknown");
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -292,25 +287,16 @@ namespace winrt::TerminalApp::implementation
|
||||
// add the keyboard shortcuts for the first 9 profiles
|
||||
if (profileIndex < 9)
|
||||
{
|
||||
// enum value for ShortcutAction::NewTabProfileX; 0==NewTabProfile0
|
||||
const auto action = static_cast<ShortcutAction>(profileIndex + static_cast<int>(ShortcutAction::NewTabProfile0));
|
||||
// First, attempt to search for the keybinding for the simple
|
||||
// NewTabProfile0-9 ShortcutActions.
|
||||
auto profileKeyChord = keyBindings.GetKeyBindingForAction(action);
|
||||
if (!profileKeyChord)
|
||||
{
|
||||
// If NewTabProfileN didn't have a binding, look for a
|
||||
// keychord that is bound to the equivalent
|
||||
// NewTab(ProfileIndex=N) action
|
||||
auto actionAndArgs = winrt::make_self<winrt::TerminalApp::implementation::ActionAndArgs>();
|
||||
actionAndArgs->Action(ShortcutAction::NewTab);
|
||||
auto newTabArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTabArgs>();
|
||||
auto newTerminalArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTerminalArgs>();
|
||||
newTerminalArgs->ProfileIndex(profileIndex);
|
||||
newTabArgs->TerminalArgs(*newTerminalArgs);
|
||||
actionAndArgs->Args(*newTabArgs);
|
||||
profileKeyChord = keyBindings.GetKeyBindingForActionWithArgs(*actionAndArgs);
|
||||
}
|
||||
// Look for a keychord that is bound to the equivalent
|
||||
// NewTab(ProfileIndex=N) action
|
||||
auto actionAndArgs = winrt::make_self<winrt::TerminalApp::implementation::ActionAndArgs>();
|
||||
actionAndArgs->Action(ShortcutAction::NewTab);
|
||||
auto newTabArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTabArgs>();
|
||||
auto newTerminalArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTerminalArgs>();
|
||||
newTerminalArgs->ProfileIndex(profileIndex);
|
||||
newTabArgs->TerminalArgs(*newTerminalArgs);
|
||||
actionAndArgs->Args(*newTabArgs);
|
||||
auto profileKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(*actionAndArgs) };
|
||||
|
||||
// make sure we find one to display
|
||||
if (profileKeyChord)
|
||||
@@ -1324,18 +1310,21 @@ namespace winrt::TerminalApp::implementation
|
||||
// copy text to dataPack
|
||||
dataPack.SetText(copiedData.Text());
|
||||
|
||||
// copy html to dataPack
|
||||
const auto htmlData = copiedData.Html();
|
||||
if (!htmlData.empty())
|
||||
if (_settings->GlobalSettings().GetCopyFormatting())
|
||||
{
|
||||
dataPack.SetHtmlFormat(htmlData);
|
||||
}
|
||||
// copy html to dataPack
|
||||
const auto htmlData = copiedData.Html();
|
||||
if (!htmlData.empty())
|
||||
{
|
||||
dataPack.SetHtmlFormat(htmlData);
|
||||
}
|
||||
|
||||
// copy rtf data to dataPack
|
||||
const auto rtfData = copiedData.Rtf();
|
||||
if (!rtfData.empty())
|
||||
{
|
||||
dataPack.SetRtf(rtfData);
|
||||
// copy rtf data to dataPack
|
||||
const auto rtfData = copiedData.Rtf();
|
||||
if (!rtfData.empty())
|
||||
{
|
||||
dataPack.SetRtf(rtfData);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
@@ -1385,14 +1374,13 @@ namespace winrt::TerminalApp::implementation
|
||||
// Method Description:
|
||||
// - Copy text from the focused terminal to the Windows Clipboard
|
||||
// Arguments:
|
||||
// - trimTrailingWhitespace: enable removing any whitespace from copied selection
|
||||
// and get text to appear on separate lines.
|
||||
// - singleLine: if enabled, copy contents as a single line of text
|
||||
// Return Value:
|
||||
// - true iff we we able to copy text (if a selection was active)
|
||||
bool TerminalPage::_CopyText(const bool trimTrailingWhitespace)
|
||||
bool TerminalPage::_CopyText(const bool singleLine)
|
||||
{
|
||||
const auto control = _GetActiveControl();
|
||||
return control.CopySelectionToClipboard(!trimTrailingWhitespace);
|
||||
return control.CopySelectionToClipboard(singleLine);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -33,16 +33,16 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</TextBlock>
|
||||
<HyperlinkButton
|
||||
x:Uid="AboutDialog_GettingStartedLink"
|
||||
NavigateUri="https://aka.ms/terminal-getting-started" />
|
||||
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125715" />
|
||||
<HyperlinkButton
|
||||
x:Uid="AboutDialog_DocumentationLink"
|
||||
NavigateUri="https://aka.ms/terminal-documentation" />
|
||||
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125416" />
|
||||
<HyperlinkButton
|
||||
x:Uid="AboutDialog_ReleaseNotesLink"
|
||||
NavigateUri="https://aka.ms/terminal-release-notes" />
|
||||
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125417" />
|
||||
<HyperlinkButton
|
||||
x:Uid="AboutDialog_PrivacyPolicyLink"
|
||||
NavigateUri="https://aka.ms/terminal-privacy-policy" />
|
||||
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125418" />
|
||||
</StackPanel>
|
||||
</ContentDialog>
|
||||
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
// THIS IS AN AUTO-GENERATED FILE! Changes to this file will be ignored.
|
||||
{
|
||||
"alwaysShowTabs": true,
|
||||
"defaultProfile": "{550ce7b8-d500-50ad-8a1a-c400c3262db3}",
|
||||
|
||||
// Launch Settings
|
||||
"initialCols": 120,
|
||||
"initialRows": 30,
|
||||
"requestedTheme": "system",
|
||||
"showTabsInTitlebar": false,
|
||||
"showTerminalTitleInTitlebar": true,
|
||||
"launchMode": "default",
|
||||
|
||||
// Selection
|
||||
"copyOnSelect": false,
|
||||
"wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502",
|
||||
|
||||
// Tab UI
|
||||
"alwaysShowTabs": true,
|
||||
"showTabsInTitlebar": true,
|
||||
"showTerminalTitleInTitlebar": true,
|
||||
"tabWidthMode": "equal",
|
||||
|
||||
// Miscellaneous
|
||||
"confirmCloseAllTabs": true,
|
||||
"theme": "system",
|
||||
"rowsToScroll": "system",
|
||||
"snapToGridOnResize": true,
|
||||
|
||||
"profiles":
|
||||
[
|
||||
{
|
||||
@@ -59,54 +73,68 @@
|
||||
],
|
||||
"keybindings":
|
||||
[
|
||||
{ "command": "closePane", "keys": [ "ctrl+shift+w" ] },
|
||||
{ "command": "closeWindow", "keys": [ "alt+f4" ] },
|
||||
{ "command": "copy", "keys": [ "ctrl+shift+c" ] },
|
||||
{ "command": "copy", "keys": [ "ctrl+insert" ] },
|
||||
{ "command": "decreaseFontSize", "keys": [ "ctrl+-" ] },
|
||||
{ "command": "duplicateTab", "keys": [ "ctrl+shift+d" ] },
|
||||
{ "command": "increaseFontSize", "keys": [ "ctrl+=" ] },
|
||||
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": [ "alt+down" ] },
|
||||
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": [ "alt+left" ] },
|
||||
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": [ "alt+right" ] },
|
||||
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": [ "alt+up" ] },
|
||||
{ "command": "newTab", "keys": [ "ctrl+shift+t" ] },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": ["ctrl+shift+1"] },
|
||||
{ "command": { "action": "newTab", "index": 1 }, "keys": ["ctrl+shift+2"] },
|
||||
{ "command": { "action": "newTab", "index": 2 }, "keys": ["ctrl+shift+3"] },
|
||||
{ "command": { "action": "newTab", "index": 3 }, "keys": ["ctrl+shift+4"] },
|
||||
{ "command": { "action": "newTab", "index": 4 }, "keys": ["ctrl+shift+5"] },
|
||||
{ "command": { "action": "newTab", "index": 5 }, "keys": ["ctrl+shift+6"] },
|
||||
{ "command": { "action": "newTab", "index": 6 }, "keys": ["ctrl+shift+7"] },
|
||||
{ "command": { "action": "newTab", "index": 7 }, "keys": ["ctrl+shift+8"] },
|
||||
{ "command": { "action": "newTab", "index": 8 }, "keys": ["ctrl+shift+9"] },
|
||||
{ "command": "nextTab", "keys": [ "ctrl+tab" ] },
|
||||
{ "command": "openNewTabDropdown", "keys": [ "ctrl+shift+space" ] },
|
||||
{ "command": "openSettings", "keys": [ "ctrl+," ] },
|
||||
{ "command": "paste", "keys": [ "ctrl+shift+v" ] },
|
||||
{ "command": "paste", "keys": [ "shift+insert" ] },
|
||||
{ "command": "prevTab", "keys": [ "ctrl+shift+tab" ] },
|
||||
{ "command": "resetFontSize", "keys": ["ctrl+0"]},
|
||||
{ "command": { "action": "resizePane", "direction": "down" }, "keys": [ "alt+shift+down" ] },
|
||||
{ "command": { "action": "resizePane", "direction": "left" }, "keys": [ "alt+shift+left" ] },
|
||||
{ "command": { "action": "resizePane", "direction": "right" }, "keys": [ "alt+shift+right" ] },
|
||||
{ "command": { "action": "resizePane", "direction": "up" }, "keys": [ "alt+shift+up" ] },
|
||||
{ "command": "scrollDown", "keys": [ "ctrl+shift+down" ] },
|
||||
{ "command": "scrollDownPage", "keys": [ "ctrl+shift+pgdn" ] },
|
||||
{ "command": "scrollUp", "keys": [ "ctrl+shift+up" ] },
|
||||
{ "command": "scrollUpPage", "keys": [ "ctrl+shift+pgup" ] },
|
||||
{ "command": { "action": "splitPane", "split": "horizontal"}, "keys": [ "alt+shift+-" ] },
|
||||
{ "command": { "action": "splitPane", "split": "vertical"}, "keys": [ "alt+shift+plus" ] },
|
||||
{ "command": { "action": "switchToTab", "index": 0 }, "keys": ["ctrl+alt+1"] },
|
||||
{ "command": { "action": "switchToTab", "index": 1 }, "keys": ["ctrl+alt+2"] },
|
||||
{ "command": { "action": "switchToTab", "index": 2 }, "keys": ["ctrl+alt+3"] },
|
||||
{ "command": { "action": "switchToTab", "index": 3 }, "keys": ["ctrl+alt+4"] },
|
||||
{ "command": { "action": "switchToTab", "index": 4 }, "keys": ["ctrl+alt+5"] },
|
||||
{ "command": { "action": "switchToTab", "index": 5 }, "keys": ["ctrl+alt+6"] },
|
||||
{ "command": { "action": "switchToTab", "index": 6 }, "keys": ["ctrl+alt+7"] },
|
||||
{ "command": { "action": "switchToTab", "index": 7 }, "keys": ["ctrl+alt+8"] },
|
||||
{ "command": { "action": "switchToTab", "index": 8 }, "keys": ["ctrl+alt+9"] },
|
||||
{ "command": "toggleFullscreen", "keys": [ "alt+enter" ] },
|
||||
{ "command": "toggleFullscreen", "keys": [ "f11" ] }
|
||||
// Application-level Keys
|
||||
{ "command": "closeWindow", "keys": "alt+f4" },
|
||||
{ "command": "toggleFullscreen", "keys": "alt+enter" },
|
||||
{ "command": "toggleFullscreen", "keys": "f11" },
|
||||
{ "command": "openNewTabDropdown", "keys": "ctrl+shift+space" },
|
||||
{ "command": "openSettings", "keys": "ctrl+," },
|
||||
{ "command": "find", "keys": "ctrl+shift+f" },
|
||||
|
||||
// Tab Management
|
||||
// "command": "closeTab" is unbound by default.
|
||||
// The closeTab command closes a tab without confirmation, even if it has multiple panes.
|
||||
{ "command": "newTab", "keys": "ctrl+shift+t" },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+shift+1" },
|
||||
{ "command": { "action": "newTab", "index": 1 }, "keys": "ctrl+shift+2" },
|
||||
{ "command": { "action": "newTab", "index": 2 }, "keys": "ctrl+shift+3" },
|
||||
{ "command": { "action": "newTab", "index": 3 }, "keys": "ctrl+shift+4" },
|
||||
{ "command": { "action": "newTab", "index": 4 }, "keys": "ctrl+shift+5" },
|
||||
{ "command": { "action": "newTab", "index": 5 }, "keys": "ctrl+shift+6" },
|
||||
{ "command": { "action": "newTab", "index": 6 }, "keys": "ctrl+shift+7" },
|
||||
{ "command": { "action": "newTab", "index": 7 }, "keys": "ctrl+shift+8" },
|
||||
{ "command": { "action": "newTab", "index": 8 }, "keys": "ctrl+shift+9" },
|
||||
{ "command": "duplicateTab", "keys": "ctrl+shift+d" },
|
||||
{ "command": "nextTab", "keys": "ctrl+tab" },
|
||||
{ "command": "prevTab", "keys": "ctrl+shift+tab" },
|
||||
{ "command": { "action": "switchToTab", "index": 0 }, "keys": "ctrl+alt+1" },
|
||||
{ "command": { "action": "switchToTab", "index": 1 }, "keys": "ctrl+alt+2" },
|
||||
{ "command": { "action": "switchToTab", "index": 2 }, "keys": "ctrl+alt+3" },
|
||||
{ "command": { "action": "switchToTab", "index": 3 }, "keys": "ctrl+alt+4" },
|
||||
{ "command": { "action": "switchToTab", "index": 4 }, "keys": "ctrl+alt+5" },
|
||||
{ "command": { "action": "switchToTab", "index": 5 }, "keys": "ctrl+alt+6" },
|
||||
{ "command": { "action": "switchToTab", "index": 6 }, "keys": "ctrl+alt+7" },
|
||||
{ "command": { "action": "switchToTab", "index": 7 }, "keys": "ctrl+alt+8" },
|
||||
{ "command": { "action": "switchToTab", "index": 8 }, "keys": "ctrl+alt+9" },
|
||||
|
||||
// Pane Management
|
||||
{ "command": "closePane", "keys": "ctrl+shift+w" },
|
||||
{ "command": { "action": "splitPane", "split": "horizontal" }, "keys": "alt+shift+-" },
|
||||
{ "command": { "action": "splitPane", "split": "vertical" }, "keys": "alt+shift+plus" },
|
||||
{ "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down" },
|
||||
{ "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left" },
|
||||
{ "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right" },
|
||||
{ "command": { "action": "resizePane", "direction": "up" }, "keys": "alt+shift+up" },
|
||||
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": "alt+down" },
|
||||
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": "alt+left" },
|
||||
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": "alt+right" },
|
||||
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": "alt+up" },
|
||||
|
||||
// Clipboard Integration
|
||||
{ "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+shift+c" },
|
||||
{ "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+insert" },
|
||||
{ "command": "paste", "keys": "ctrl+shift+v" },
|
||||
{ "command": "paste", "keys": "shift+insert" },
|
||||
|
||||
// Scrollback
|
||||
{ "command": "scrollDown", "keys": "ctrl+shift+down" },
|
||||
{ "command": "scrollDownPage", "keys": "ctrl+shift+pgdn" },
|
||||
{ "command": "scrollUp", "keys": "ctrl+shift+up" },
|
||||
{ "command": "scrollUpPage", "keys": "ctrl+shift+pgup" },
|
||||
|
||||
// Visual Adjustments
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+=" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 }, "keys": "ctrl+-" },
|
||||
{ "command": "resetFontSize", "keys": "ctrl+0" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
// THIS IS AN AUTO-GENERATED FILE! Changes to this file will be ignored.
|
||||
{
|
||||
"alwaysShowTabs": true,
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
|
||||
// Launch Settings
|
||||
"initialCols": 120,
|
||||
"initialRows": 30,
|
||||
"requestedTheme": "system",
|
||||
"launchMode": "default",
|
||||
|
||||
// Selection
|
||||
"copyOnSelect": false,
|
||||
"wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502",
|
||||
|
||||
// Tab UI
|
||||
"alwaysShowTabs": true,
|
||||
"showTabsInTitlebar": true,
|
||||
"showTerminalTitleInTitlebar": true,
|
||||
"tabWidthMode": "equal",
|
||||
"snapToGridOnResize": true,
|
||||
"wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502",
|
||||
|
||||
// Miscellaneous
|
||||
"confirmCloseAllTabs": true,
|
||||
"theme": "system",
|
||||
"rowsToScroll": "system",
|
||||
"snapToGridOnResize": true,
|
||||
|
||||
"profiles":
|
||||
[
|
||||
@@ -18,41 +29,45 @@
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"name": "Windows PowerShell",
|
||||
"commandline": "powershell.exe",
|
||||
"hidden": false,
|
||||
"startingDirectory": "%USERPROFILE%",
|
||||
"closeOnExit": "graceful",
|
||||
"icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
|
||||
"colorScheme": "Campbell Powershell",
|
||||
"antialiasingMode": "grayscale",
|
||||
"closeOnExit": "graceful",
|
||||
"cursorShape": "bar",
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 12,
|
||||
"hidden": false,
|
||||
"historySize": 9001,
|
||||
"icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
|
||||
"padding": "8, 8, 8, 8",
|
||||
"snapOnInput": true,
|
||||
"useAcrylic": false,
|
||||
"antialiasingMode": "grayscale"
|
||||
"startingDirectory": "%USERPROFILE%",
|
||||
"useAcrylic": false
|
||||
},
|
||||
{
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"name": "cmd",
|
||||
"name": "Command Prompt",
|
||||
"commandline": "cmd.exe",
|
||||
"hidden": false,
|
||||
"startingDirectory": "%USERPROFILE%",
|
||||
"closeOnExit": "graceful",
|
||||
"icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png",
|
||||
"colorScheme": "Campbell",
|
||||
"antialiasingMode": "grayscale",
|
||||
"closeOnExit": "graceful",
|
||||
"cursorShape": "bar",
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 12,
|
||||
"hidden": false,
|
||||
"historySize": 9001,
|
||||
"icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png",
|
||||
"padding": "8, 8, 8, 8",
|
||||
"snapOnInput": true,
|
||||
"useAcrylic": false,
|
||||
"antialiasingMode": "grayscale"
|
||||
"startingDirectory": "%USERPROFILE%",
|
||||
"useAcrylic": false
|
||||
}
|
||||
],
|
||||
"schemes":
|
||||
[
|
||||
// A profile can override the following color scheme values:
|
||||
// - "foreground"
|
||||
// - "background"
|
||||
// - "cursorColor"
|
||||
{
|
||||
"name": "Campbell",
|
||||
"foreground": "#CCCCCC",
|
||||
@@ -210,17 +225,17 @@
|
||||
],
|
||||
"keybindings":
|
||||
[
|
||||
{ "command": "closePane", "keys": "ctrl+shift+w" },
|
||||
// Application-level Keys
|
||||
{ "command": "closeWindow", "keys": "alt+f4" },
|
||||
{ "command": "copy", "keys": "ctrl+shift+c" },
|
||||
{ "command": "copy", "keys": "ctrl+insert" },
|
||||
{ "command": "decreaseFontSize", "keys": "ctrl+-" },
|
||||
{ "command": "duplicateTab", "keys": "ctrl+shift+d" },
|
||||
{ "command": "increaseFontSize", "keys": "ctrl+=" },
|
||||
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": "alt+down" },
|
||||
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": "alt+left" },
|
||||
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": "alt+right" },
|
||||
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": "alt+up" },
|
||||
{ "command": "toggleFullscreen", "keys": "alt+enter" },
|
||||
{ "command": "toggleFullscreen", "keys": "f11" },
|
||||
{ "command": "openNewTabDropdown", "keys": "ctrl+shift+space" },
|
||||
{ "command": "openSettings", "keys": "ctrl+," },
|
||||
{ "command": "find", "keys": "ctrl+shift+f" },
|
||||
|
||||
// Tab Management
|
||||
// "command": "closeTab" is unbound by default.
|
||||
// The closeTab command closes a tab without confirmation, even if it has multiple panes.
|
||||
{ "command": "newTab", "keys": "ctrl+shift+t" },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+shift+1" },
|
||||
{ "command": { "action": "newTab", "index": 1 }, "keys": "ctrl+shift+2" },
|
||||
@@ -231,23 +246,9 @@
|
||||
{ "command": { "action": "newTab", "index": 6 }, "keys": "ctrl+shift+7" },
|
||||
{ "command": { "action": "newTab", "index": 7 }, "keys": "ctrl+shift+8" },
|
||||
{ "command": { "action": "newTab", "index": 8 }, "keys": "ctrl+shift+9" },
|
||||
{ "command": "duplicateTab", "keys": "ctrl+shift+d" },
|
||||
{ "command": "nextTab", "keys": "ctrl+tab" },
|
||||
{ "command": "openNewTabDropdown", "keys": "ctrl+shift+space" },
|
||||
{ "command": "openSettings", "keys": "ctrl+," },
|
||||
{ "command": "paste", "keys": "ctrl+shift+v" },
|
||||
{ "command": "paste", "keys": "shift+insert" },
|
||||
{ "command": "prevTab", "keys": "ctrl+shift+tab" },
|
||||
{ "command": "resetFontSize", "keys": "ctrl+0" },
|
||||
{ "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down" },
|
||||
{ "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left" },
|
||||
{ "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right" },
|
||||
{ "command": { "action": "resizePane", "direction": "up" }, "keys": "alt+shift+up" },
|
||||
{ "command": "scrollDown", "keys": "ctrl+shift+down" },
|
||||
{ "command": "scrollDownPage", "keys": "ctrl+shift+pgdn" },
|
||||
{ "command": "scrollUp", "keys": "ctrl+shift+up" },
|
||||
{ "command": "scrollUpPage", "keys": "ctrl+shift+pgup" },
|
||||
{ "command": { "action": "splitPane", "split": "horizontal"}, "keys": "alt+shift+-" },
|
||||
{ "command": { "action": "splitPane", "split": "vertical"}, "keys": "alt+shift+plus" },
|
||||
{ "command": { "action": "switchToTab", "index": 0 }, "keys": "ctrl+alt+1" },
|
||||
{ "command": { "action": "switchToTab", "index": 1 }, "keys": "ctrl+alt+2" },
|
||||
{ "command": { "action": "switchToTab", "index": 2 }, "keys": "ctrl+alt+3" },
|
||||
@@ -257,8 +258,35 @@
|
||||
{ "command": { "action": "switchToTab", "index": 6 }, "keys": "ctrl+alt+7" },
|
||||
{ "command": { "action": "switchToTab", "index": 7 }, "keys": "ctrl+alt+8" },
|
||||
{ "command": { "action": "switchToTab", "index": 8 }, "keys": "ctrl+alt+9" },
|
||||
{ "command": "find", "keys": "ctrl+shift+f" },
|
||||
{ "command": "toggleFullscreen", "keys": "alt+enter" },
|
||||
{ "command": "toggleFullscreen", "keys": "f11" }
|
||||
|
||||
// Pane Management
|
||||
{ "command": "closePane", "keys": "ctrl+shift+w" },
|
||||
{ "command": { "action": "splitPane", "split": "horizontal" }, "keys": "alt+shift+-" },
|
||||
{ "command": { "action": "splitPane", "split": "vertical" }, "keys": "alt+shift+plus" },
|
||||
{ "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down" },
|
||||
{ "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left" },
|
||||
{ "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right" },
|
||||
{ "command": { "action": "resizePane", "direction": "up" }, "keys": "alt+shift+up" },
|
||||
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": "alt+down" },
|
||||
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": "alt+left" },
|
||||
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": "alt+right" },
|
||||
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": "alt+up" },
|
||||
|
||||
// Clipboard Integration
|
||||
{ "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+shift+c" },
|
||||
{ "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+insert" },
|
||||
{ "command": "paste", "keys": "ctrl+shift+v" },
|
||||
{ "command": "paste", "keys": "shift+insert" },
|
||||
|
||||
// Scrollback
|
||||
{ "command": "scrollDown", "keys": "ctrl+shift+down" },
|
||||
{ "command": "scrollDownPage", "keys": "ctrl+shift+pgdn" },
|
||||
{ "command": "scrollUp", "keys": "ctrl+shift+up" },
|
||||
{ "command": "scrollUpPage", "keys": "ctrl+shift+pgup" },
|
||||
|
||||
// Visual Adjustments
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+=" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": -1 }, "keys": "ctrl+-" },
|
||||
{ "command": "resetFontSize", "keys": "ctrl+0" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#define BLOCK_TIL
|
||||
#include <LibraryIncludes.h>
|
||||
// This is inexplicable, but for whatever reason, cppwinrt conflicts with the
|
||||
// SDK definition of this function, so the only fix is to undef it.
|
||||
@@ -63,3 +65,6 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
|
||||
#include <CLI11/CLI11.hpp>
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#include "til.h"
|
||||
|
||||
@@ -1,40 +1,72 @@
|
||||
// This file was initially generated by %PRODUCT% %VERSION%
|
||||
// It should still be usable in newer versions, but newer versions might have additional
|
||||
// settings, help text, or changes that you will not see unless you clear this file
|
||||
// and let us generate a new one for you.
|
||||
|
||||
// To view the default settings, hold "alt" while clicking on the "Settings" button.
|
||||
// For documentation on these settings, see: https://aka.ms/terminal-documentation
|
||||
|
||||
{
|
||||
"$schema": "https://aka.ms/terminal-profiles-schema",
|
||||
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"defaultProfile": "%DEFAULT_PROFILE%",
|
||||
|
||||
// You can add more global application settings here.
|
||||
// To learn more about global settings, visit https://aka.ms/terminal-global-settings
|
||||
|
||||
// If enabled, selections are automatically copied to your clipboard.
|
||||
"copyOnSelect": false,
|
||||
|
||||
// A profile specifies a command to execute paired with information about how it should look and feel.
|
||||
// Each one of them will appear in the 'New Tab' dropdown,
|
||||
// and can be invoked from the commandline with `wt.exe -p xxx`
|
||||
// To learn more about profiles, visit https://aka.ms/terminal-profile-settings
|
||||
"profiles":
|
||||
{
|
||||
"defaults":
|
||||
{
|
||||
// Put settings here that you want to apply to all profiles
|
||||
// Put settings here that you want to apply to all profiles.
|
||||
},
|
||||
"list":
|
||||
[
|
||||
{
|
||||
// Make changes here to the powershell.exe profile
|
||||
// Make changes here to the powershell.exe profile.
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"name": "Windows PowerShell",
|
||||
"commandline": "powershell.exe",
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
// Make changes here to the cmd.exe profile
|
||||
// Make changes here to the cmd.exe profile.
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"name": "cmd",
|
||||
"name": "%COMMAND_PROMPT_LOCALIZED_NAME%",
|
||||
"commandline": "cmd.exe",
|
||||
"hidden": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Add custom color schemes to this array
|
||||
// Add custom color schemes to this array.
|
||||
// To learn more about color schemes, visit https://aka.ms/terminal-color-schemes
|
||||
"schemes": [],
|
||||
|
||||
// Add any keybinding overrides to this array.
|
||||
// To unbind a default keybinding, set the command to "unbound"
|
||||
"keybindings": []
|
||||
// Add custom keybindings to this array.
|
||||
// To unbind a key combination from your defaults.json, set the command to "unbound".
|
||||
// To learn more about keybindings, visit https://aka.ms/terminal-keybindings
|
||||
"keybindings":
|
||||
[
|
||||
// Copy and paste are bound to Ctrl+Shift+C and Ctrl+Shift+V in your defaults.json.
|
||||
// These two lines additionally bind them to Ctrl+C and Ctrl+V.
|
||||
// To learn more about selection, visit https://aka.ms/terminal-selection
|
||||
{ "command": {"action": "copy", "singleLine": false }, "keys": "ctrl+c" },
|
||||
{ "command": "paste", "keys": "ctrl+v" },
|
||||
|
||||
// Press Ctrl+Shift+F to open the search box
|
||||
{ "command": "find", "keys": "ctrl+shift+f" },
|
||||
|
||||
// Press Alt+Shift+D to open a new pane.
|
||||
// - "split": "auto" makes this pane open in the direction that provides the most surface area.
|
||||
// - "splitMode": "duplicate" makes the new pane use the focused pane's profile.
|
||||
// To learn more about panes, visit https://aka.ms/terminal-panes
|
||||
{ "command": { "action": "splitPane", "split": "auto", "splitMode": "duplicate" }, "keys": "alt+shift+d" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
|
||||
#define BLOCK_GSL
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#define BLOCK_TIL
|
||||
#include <LibraryIncludes.h>
|
||||
|
||||
// Must be included before any WinRT headers.
|
||||
#include <unknwn.h>
|
||||
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <wil/cppwinrt.h>
|
||||
|
||||
#include "winrt/Windows.Foundation.h"
|
||||
#include "winrt/Windows.Security.Credentials.h"
|
||||
#include "winrt/Windows.Foundation.Collections.h"
|
||||
#include <Windows.h>
|
||||
@@ -27,3 +28,5 @@
|
||||
#include <TraceLoggingProvider.h>
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalConnectionProvider);
|
||||
#include <telemetry/ProjectTelemetry.h>
|
||||
|
||||
#include "til.h"
|
||||
|
||||
16
src/cascadia/TerminalControl/IMouseWheelListener.idl
Normal file
16
src/cascadia/TerminalControl/IMouseWheelListener.idl
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Microsoft.Terminal.TerminalControl
|
||||
{
|
||||
|
||||
// This interface is a hack for GH#979. Controls should implement this
|
||||
// interface to be able to be notified of mousewheel events even on devices
|
||||
// who's trackpads won't scroll inactive windows.
|
||||
|
||||
[uuid("65b8b8c5-988f-43ff-aba9-e89368da1598")]
|
||||
interface IMouseWheelListener
|
||||
{
|
||||
Boolean OnMouseWheel(Windows.Foundation.Point coord, Int32 delta);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
TSFInputControl::TSFInputControl() :
|
||||
_editContext{ nullptr },
|
||||
_inComposition{ false },
|
||||
_activeTextStart{ 0 }
|
||||
_activeTextStart{ 0 },
|
||||
_focused{ false },
|
||||
_currentTerminalCursorPos{ 0, 0 },
|
||||
_currentCanvasWidth{ 0.0 },
|
||||
_currentTextBlockHeight{ 0.0 },
|
||||
_currentTextBounds{ 0, 0, 0, 0 },
|
||||
_currentControlBounds{ 0, 0, 0, 0 }
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
@@ -58,7 +64,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
// Explicitly disconnect the LayoutRequested handler -- it can cause problems during application teardown.
|
||||
// See GH#4159 for more info.
|
||||
// Also disconnect compositionCompleted and textUpdating explicitly. It seems to occasionally cause problems if
|
||||
// a composition is active during application teardown.
|
||||
_layoutRequestedRevoker.revoke();
|
||||
_compositionCompletedRevoker.revoke();
|
||||
_textUpdatingRevoker.revoke();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -73,6 +83,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
if (_editContext != nullptr)
|
||||
{
|
||||
_editContext.NotifyFocusEnter();
|
||||
_focused = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +99,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
if (_editContext != nullptr)
|
||||
{
|
||||
_editContext.NotifyFocusLeave();
|
||||
_focused = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,28 +126,51 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handler for LayoutRequested event by CoreEditContext responsible
|
||||
// for returning the current position the IME should be placed
|
||||
// in screen coordinates on the screen. TSFInputControls internal
|
||||
// XAML controls (TextBlock/Canvas) are also positioned and updated.
|
||||
// NOTE: documentation says application should handle this event
|
||||
// - Redraw the canvas if certain dimensions have changed since the last
|
||||
// redraw. This includes the Terminal cursor position, the Canvas width, and the TextBlock height.
|
||||
// Arguments:
|
||||
// - sender: CoreTextEditContext sending the request.
|
||||
// - args: CoreTextLayoutRequestedEventArgs to be updated with position information.
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TSFInputControl::_layoutRequestedHandler(CoreTextEditContext sender, CoreTextLayoutRequestedEventArgs const& args)
|
||||
void TSFInputControl::TryRedrawCanvas()
|
||||
{
|
||||
auto request = args.Request();
|
||||
|
||||
// Get window in screen coordinates, this is the entire window including tabs
|
||||
const auto windowBounds = CoreWindow::GetForCurrentThread().Bounds();
|
||||
if (!_focused)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the cursor position in text buffer position
|
||||
auto cursorArgs = winrt::make_self<CursorPositionEventArgs>();
|
||||
_CurrentCursorPositionHandlers(*this, *cursorArgs);
|
||||
const COORD cursorPos = { ::base::ClampedNumeric<short>(cursorArgs->CurrentPosition().X), ::base::ClampedNumeric<short>(cursorArgs->CurrentPosition().Y) };
|
||||
const til::point cursorPos{ gsl::narrow_cast<ptrdiff_t>(cursorArgs->CurrentPosition().X), gsl::narrow_cast<ptrdiff_t>(cursorArgs->CurrentPosition().Y) };
|
||||
|
||||
const double actualCanvasWidth = Canvas().ActualWidth();
|
||||
|
||||
const double actualTextBlockHeight = TextBlock().ActualHeight();
|
||||
|
||||
if (_currentTerminalCursorPos == cursorPos &&
|
||||
_currentCanvasWidth == actualCanvasWidth &&
|
||||
_currentTextBlockHeight == actualTextBlockHeight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentTerminalCursorPos = cursorPos;
|
||||
_currentCanvasWidth = actualCanvasWidth;
|
||||
_currentTextBlockHeight = actualTextBlockHeight;
|
||||
|
||||
_RedrawCanvas();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Redraw the Canvas and update the current Text Bounds and Control Bounds for
|
||||
// the CoreTextEditContext.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TSFInputControl::_RedrawCanvas()
|
||||
{
|
||||
// Get Font Info as we use this is the pixel size for characters in the display
|
||||
auto fontArgs = winrt::make_self<FontInfoEventArgs>();
|
||||
_CurrentFontInfoHandlers(*this, *fontArgs);
|
||||
@@ -145,8 +180,27 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
// Convert text buffer cursor position to client coordinate position within the window
|
||||
COORD clientCursorPos;
|
||||
clientCursorPos.X = ::base::ClampMul(cursorPos.X, ::base::ClampedNumeric<short>(fontWidth));
|
||||
clientCursorPos.Y = ::base::ClampMul(cursorPos.Y, ::base::ClampedNumeric<short>(fontHeight));
|
||||
clientCursorPos.X = ::base::ClampMul(_currentTerminalCursorPos.x(), ::base::ClampedNumeric<ptrdiff_t>(fontWidth));
|
||||
clientCursorPos.Y = ::base::ClampMul(_currentTerminalCursorPos.y(), ::base::ClampedNumeric<ptrdiff_t>(fontHeight));
|
||||
|
||||
// position textblock to cursor position
|
||||
Canvas().SetLeft(TextBlock(), clientCursorPos.X);
|
||||
Canvas().SetTop(TextBlock(), clientCursorPos.Y);
|
||||
|
||||
// calculate FontSize in pixels from DPIs
|
||||
const double fontSizePx = (fontHeight * 72) / USER_DEFAULT_SCREEN_DPI;
|
||||
TextBlock().FontSize(fontSizePx);
|
||||
TextBlock().FontFamily(Media::FontFamily(fontArgs->FontFace()));
|
||||
|
||||
const auto widthToTerminalEnd = _currentCanvasWidth - ::base::ClampedNumeric<double>(clientCursorPos.X);
|
||||
// Make sure that we're setting the MaxWidth to a positive number - a
|
||||
// negative number here will crash us in mysterious ways with a useless
|
||||
// stack trace
|
||||
const auto newMaxWidth = std::max<double>(0.0, widthToTerminalEnd);
|
||||
TextBlock().MaxWidth(newMaxWidth);
|
||||
|
||||
// Get window in screen coordinates, this is the entire window including tabs
|
||||
const auto windowBounds = CoreWindow::GetForCurrentThread().Bounds();
|
||||
|
||||
// Convert from client coordinate to screen coordinate by adding window position
|
||||
COORD screenCursorPos;
|
||||
@@ -162,33 +216,35 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
// Get scale factor for view
|
||||
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
|
||||
const auto yOffset = ::base::ClampedNumeric<float>(_currentTextBlockHeight) - fontHeight;
|
||||
const auto textBottom = ::base::ClampedNumeric<float>(screenCursorPos.Y) + yOffset;
|
||||
|
||||
// position textblock to cursor position
|
||||
Canvas().SetLeft(TextBlock(), clientCursorPos.X);
|
||||
Canvas().SetTop(TextBlock(), ::base::ClampedNumeric<double>(clientCursorPos.Y));
|
||||
_currentTextBounds = ScaleRect(Rect(screenCursorPos.X, textBottom, 0, fontHeight), scaleFactor);
|
||||
_currentControlBounds = ScaleRect(Rect(screenCursorPos.X, screenCursorPos.Y, 0, fontHeight), scaleFactor);
|
||||
}
|
||||
|
||||
// calculate FontSize in pixels from DIPs
|
||||
const double fontSizePx = (fontHeight * 72) / USER_DEFAULT_SCREEN_DPI;
|
||||
TextBlock().FontSize(fontSizePx);
|
||||
TextBlock().FontFamily(Media::FontFamily(fontArgs->FontFace()));
|
||||
// Method Description:
|
||||
// - Handler for LayoutRequested event by CoreEditContext responsible
|
||||
// for returning the current position the IME should be placed
|
||||
// in screen coordinates on the screen. TSFInputControls internal
|
||||
// XAML controls (TextBlock/Canvas) are also positioned and updated.
|
||||
// NOTE: documentation says application should handle this event
|
||||
// Arguments:
|
||||
// - sender: CoreTextEditContext sending the request.
|
||||
// - args: CoreTextLayoutRequestedEventArgs to be updated with position information.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TSFInputControl::_layoutRequestedHandler(CoreTextEditContext sender, CoreTextLayoutRequestedEventArgs const& args)
|
||||
{
|
||||
auto request = args.Request();
|
||||
|
||||
const auto canvasActualWidth = Canvas().ActualWidth();
|
||||
const auto widthToTerminalEnd = canvasActualWidth - ::base::ClampedNumeric<double>(clientCursorPos.X);
|
||||
// Make sure that we're setting the MaxWidth to a positive number - a
|
||||
// negative number here will crash us in mysterious ways with a useless
|
||||
// stack trace
|
||||
const auto newMaxWidth = std::max<double>(0.0, widthToTerminalEnd);
|
||||
TextBlock().MaxWidth(newMaxWidth);
|
||||
TryRedrawCanvas();
|
||||
|
||||
// Set the text block bounds
|
||||
const auto yOffset = ::base::ClampedNumeric<float>(TextBlock().ActualHeight()) - fontHeight;
|
||||
const auto textBottom = ::base::ClampedNumeric<float>(screenCursorPos.Y) + yOffset;
|
||||
Rect selectionRect = Rect(screenCursorPos.X, textBottom, 0, fontHeight);
|
||||
request.LayoutBounds().TextBounds(ScaleRect(selectionRect, scaleFactor));
|
||||
request.LayoutBounds().TextBounds(_currentTextBounds);
|
||||
|
||||
// Set the control bounds of the whole control
|
||||
Rect controlRect = Rect(screenCursorPos.X, screenCursorPos.Y, 0, fontHeight);
|
||||
request.LayoutBounds().ControlBounds(ScaleRect(controlRect, scaleFactor));
|
||||
request.LayoutBounds().ControlBounds(_currentControlBounds);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void NotifyFocusEnter();
|
||||
void NotifyFocusLeave();
|
||||
void ClearBuffer();
|
||||
void TryRedrawCanvas();
|
||||
|
||||
void Close();
|
||||
|
||||
@@ -73,6 +74,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
bool _inComposition;
|
||||
size_t _activeTextStart;
|
||||
void _SendAndClearText();
|
||||
void _RedrawCanvas();
|
||||
bool _focused;
|
||||
|
||||
til::point _currentTerminalCursorPos;
|
||||
double _currentCanvasWidth;
|
||||
double _currentTextBlockHeight;
|
||||
winrt::Windows::Foundation::Rect _currentControlBounds;
|
||||
winrt::Windows::Foundation::Rect _currentTextBounds;
|
||||
};
|
||||
}
|
||||
namespace winrt::Microsoft::Terminal::TerminalControl::factory_implementation
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
void NotifyFocusEnter();
|
||||
void NotifyFocusLeave();
|
||||
void ClearBuffer();
|
||||
void TryRedrawCanvas();
|
||||
|
||||
void Close();
|
||||
}
|
||||
|
||||
@@ -86,6 +86,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
auto pfnScrollPositionChanged = std::bind(&TermControl::_TerminalScrollPositionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
_terminal->SetScrollPositionChangedCallback(pfnScrollPositionChanged);
|
||||
|
||||
auto pfnTerminalCursorPositionChanged = std::bind(&TermControl::_TerminalCursorPositionChanged, this);
|
||||
_terminal->SetCursorPositionChangedCallback(pfnTerminalCursorPositionChanged);
|
||||
|
||||
// This event is explicitly revoked in the destructor: does not need weak_ref
|
||||
auto onReceiveOutputFn = [this](const hstring str) {
|
||||
_terminal->Write(str);
|
||||
@@ -481,14 +484,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged()
|
||||
{
|
||||
{ // lock scope
|
||||
auto terminalLock = _terminal->LockForReading();
|
||||
if (!_initializedTerminal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This event is only registered during terminal initialization,
|
||||
// so we don't need to check _initializedTerminal.
|
||||
// We also don't lock for things that come back from the renderer.
|
||||
auto chain = _renderEngine->GetSwapChain();
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
@@ -496,8 +494,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
auto terminalLock = _terminal->LockForWriting();
|
||||
|
||||
_AttachDxgiSwapChainToXaml(chain.Get());
|
||||
}
|
||||
}
|
||||
@@ -636,11 +632,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// becomes a no-op.
|
||||
this->Focus(FocusState::Programmatic);
|
||||
|
||||
_connection.Start();
|
||||
_initializedTerminal = true;
|
||||
} // scope for TerminalLock
|
||||
// call this event dispatcher outside of lock
|
||||
|
||||
// Start the connection outside of lock, because it could
|
||||
// start writing output immediately.
|
||||
_connection.Start();
|
||||
|
||||
// Likewise, run the event handlers outside of lock (they could
|
||||
// be reentrant)
|
||||
_InitializedHandlers(*this, nullptr);
|
||||
return true;
|
||||
}
|
||||
@@ -654,8 +654,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
const auto ch = e.Character();
|
||||
|
||||
const bool handled = _terminal->SendCharEvent(ch);
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
const bool handled = _terminal->SendCharEvent(ch, scanCode, modifiers);
|
||||
e.Handled(handled);
|
||||
}
|
||||
|
||||
@@ -1169,6 +1170,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - Event handler for the PointerWheelChanged event. This is raised in
|
||||
// response to mouse wheel changes. Depending upon what modifier keys are
|
||||
// pressed, different actions will take place.
|
||||
// - Primarily just takes the data from the PointerRoutedEventArgs and uses
|
||||
// it to call _DoMouseWheel, see _DoMouseWheel for more details.
|
||||
// Arguments:
|
||||
// - args: the event args containing information about t`he mouse wheel event.
|
||||
void TermControl::_MouseWheelHandler(Windows::Foundation::IInspectable const& /*sender*/,
|
||||
@@ -1180,22 +1183,49 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
const auto point = args.GetCurrentPoint(*this);
|
||||
auto result = _DoMouseWheel(point.Position(),
|
||||
ControlKeyStates{ args.KeyModifiers() },
|
||||
point.Properties().MouseWheelDelta(),
|
||||
point.Properties().IsLeftButtonPressed());
|
||||
if (result)
|
||||
{
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Actually handle a scrolling event, whether from a mouse wheel or a
|
||||
|
||||
// touchpad scroll. Depending upon what modifier keys are pressed,
|
||||
// different actions will take place.
|
||||
// * Attempts to first dispatch the mouse scroll as a VT event
|
||||
// * If Ctrl+Shift are pressed, then attempts to change our opacity
|
||||
// * If just Ctrl is pressed, we'll attempt to "zoom" by changing our font size
|
||||
// * Otherwise, just scrolls the content of the viewport
|
||||
// Arguments:
|
||||
// - point: the location of the mouse during this event
|
||||
// - modifiers: The modifiers pressed during this event, in the form of a VirtualKeyModifiers
|
||||
// - delta: the mouse wheel delta that triggered this event.
|
||||
bool TermControl::_DoMouseWheel(const Windows::Foundation::Point point,
|
||||
const ControlKeyStates modifiers,
|
||||
const int32_t delta,
|
||||
const bool isLeftButtonPressed)
|
||||
{
|
||||
if (_CanSendVTMouseInput())
|
||||
{
|
||||
_TrySendMouseEvent(point);
|
||||
args.Handled(true);
|
||||
return;
|
||||
// Most mouse event handlers call
|
||||
// _TrySendMouseEvent(point);
|
||||
// here with a PointerPoint. However, as of #979, we don't have a
|
||||
// PointerPoint to work with. So, we're just going to do a
|
||||
// mousewheel event manually
|
||||
return _terminal->SendMouseEvent(_GetTerminalPosition(point),
|
||||
WM_MOUSEWHEEL,
|
||||
_GetPressedModifierKeys(),
|
||||
::base::saturated_cast<short>(delta));
|
||||
}
|
||||
|
||||
const auto delta = point.Properties().MouseWheelDelta();
|
||||
|
||||
// Get the state of the Ctrl & Shift keys
|
||||
// static_cast to a uint32_t because we can't use the WI_IsFlagSet macro
|
||||
// directly with a VirtualKeyModifiers
|
||||
const auto modifiers = static_cast<uint32_t>(args.KeyModifiers());
|
||||
const auto ctrlPressed = WI_IsFlagSet(modifiers, static_cast<uint32_t>(VirtualKeyModifiers::Control));
|
||||
const auto shiftPressed = WI_IsFlagSet(modifiers, static_cast<uint32_t>(VirtualKeyModifiers::Shift));
|
||||
const auto ctrlPressed = modifiers.IsCtrlPressed();
|
||||
const auto shiftPressed = modifiers.IsShiftPressed();
|
||||
|
||||
if (ctrlPressed && shiftPressed)
|
||||
{
|
||||
@@ -1207,8 +1237,25 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
_MouseScrollHandler(delta, point);
|
||||
_MouseScrollHandler(delta, point, isLeftButtonPressed);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This is part of the solution to GH#979
|
||||
// - Manually handle a scrolling event. This is used to help support
|
||||
// scrolling on devices where the touchpad doesn't correctly handle
|
||||
// scrolling inactive windows.
|
||||
// Arguments:
|
||||
// - location: the location of the mouse during this event. This location is
|
||||
// relative to the origin of the control
|
||||
// - delta: the mouse wheel delta that triggered this event.
|
||||
bool TermControl::OnMouseWheel(const Windows::Foundation::Point location,
|
||||
const int32_t delta)
|
||||
{
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
return _DoMouseWheel(location, modifiers, delta, false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1281,7 +1328,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - Scroll the visible viewport in response to a mouse wheel event.
|
||||
// Arguments:
|
||||
// - mouseDelta: the mouse wheel delta that triggered this event.
|
||||
void TermControl::_MouseScrollHandler(const double mouseDelta, Windows::UI::Input::PointerPoint const& pointerPoint)
|
||||
// - point: the location of the mouse during this event
|
||||
// - isLeftButtonPressed: true iff the left mouse button was pressed during this event.
|
||||
void TermControl::_MouseScrollHandler(const double mouseDelta,
|
||||
const Windows::Foundation::Point point,
|
||||
const bool isLeftButtonPressed)
|
||||
{
|
||||
const auto currentOffset = ScrollBar().Value();
|
||||
|
||||
@@ -1298,11 +1349,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// for us.
|
||||
ScrollBar().Value(newValue);
|
||||
|
||||
if (_terminal->IsSelectionActive() && pointerPoint.Properties().IsLeftButtonPressed())
|
||||
if (_terminal->IsSelectionActive() && isLeftButtonPressed)
|
||||
{
|
||||
// If user is mouse selecting and scrolls, they then point at new character.
|
||||
// Make sure selection reflects that immediately.
|
||||
_SetEndSelectionPointAtCursor(pointerPoint.Position());
|
||||
_SetEndSelectionPointAtCursor(point);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1566,7 +1617,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// concerned with initialization process. Value forwarded to event handler.
|
||||
void TermControl::_UpdateFont(const bool initialUpdate)
|
||||
{
|
||||
const int newDpi = static_cast<int>(static_cast<double>(USER_DEFAULT_SCREEN_DPI) * SwapChainPanel().CompositionScaleX());
|
||||
const float newDpi = static_cast<float>(static_cast<double>(USER_DEFAULT_SCREEN_DPI) * SwapChainPanel().CompositionScaleX());
|
||||
|
||||
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
|
||||
// actually fail. We need a way to gracefully fallback.
|
||||
@@ -1629,7 +1680,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
if (_renderEngine)
|
||||
{
|
||||
const auto scale = sender.CompositionScaleX();
|
||||
const auto dpi = (int)(scale * USER_DEFAULT_SCREEN_DPI);
|
||||
const auto dpi = (float)(scale * static_cast<float>(USER_DEFAULT_SCREEN_DPI));
|
||||
|
||||
// TODO: MSFT: 21169071 - Shouldn't this all happen through _renderer and trigger the invalidate automatically on DPI change?
|
||||
THROW_IF_FAILED(_renderEngine->UpdateDpi(dpi));
|
||||
@@ -1793,6 +1844,49 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Tells TSFInputControl to redraw the Canvas/TextBlock so it'll update
|
||||
// to be where the current cursor position is.
|
||||
// Arguments:
|
||||
// - N/A
|
||||
winrt::fire_and_forget TermControl::_TerminalCursorPositionChanged()
|
||||
{
|
||||
bool expectedFalse{ false };
|
||||
if (!_coroutineDispatchStateUpdateInProgress.compare_exchange_weak(expectedFalse, true))
|
||||
{
|
||||
// somebody's already in here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_closing.load())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto dispatcher{ Dispatcher() }; // cache a strong ref to this in case TermControl dies
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
// Muffle 2: Muffle Harder
|
||||
// If we're the lucky coroutine who gets through, we'll still wait 100ms to clog
|
||||
// the atomic above so we don't service the cursor update too fast. If we get through
|
||||
// and finish processing the update quickly but similar requests are still beating
|
||||
// down the door above in the atomic, we may still update the cursor way more than
|
||||
// is visible to anyone's eye, which is a waste of effort.
|
||||
static constexpr auto CursorUpdateQuiesceTime{ std::chrono::milliseconds(100) };
|
||||
co_await winrt::resume_after(CursorUpdateQuiesceTime);
|
||||
|
||||
co_await winrt::resume_foreground(dispatcher);
|
||||
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
if (!_closing.load())
|
||||
{
|
||||
TSFInputControl().TryRedrawCanvas();
|
||||
}
|
||||
_coroutineDispatchStateUpdateInProgress.store(false);
|
||||
}
|
||||
}
|
||||
|
||||
hstring TermControl::Title()
|
||||
{
|
||||
hstring hstr{ _terminal->GetConsoleTitle() };
|
||||
@@ -1809,8 +1903,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// Windows Clipboard (CascadiaWin32:main.cpp).
|
||||
// - CopyOnSelect does NOT clear the selection
|
||||
// Arguments:
|
||||
// - collapseText: collapse all of the text to one line
|
||||
bool TermControl::CopySelectionToClipboard(bool collapseText)
|
||||
// - singleLine: collapse all of the text to one line
|
||||
bool TermControl::CopySelectionToClipboard(bool singleLine)
|
||||
{
|
||||
if (_closing)
|
||||
{
|
||||
@@ -1827,7 +1921,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
_selectionNeedsToBeCopied = false;
|
||||
|
||||
// extract text from buffer
|
||||
const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(collapseText);
|
||||
const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(singleLine);
|
||||
|
||||
// convert text: vector<string> --> string
|
||||
std::wstring textData;
|
||||
@@ -1876,6 +1970,26 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
_clipboardPasteHandlers(*this, *pasteArgs);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Asynchronously close our connection. The Connection will likely wait
|
||||
// until the attached process terminates before Close returns. If that's
|
||||
// the case, we don't want to block the UI thread waiting on that process
|
||||
// handle.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TermControl::_AsyncCloseConnection()
|
||||
{
|
||||
if (auto localConnection{ std::exchange(_connection, nullptr) })
|
||||
{
|
||||
// Close the connection on the background thread.
|
||||
co_await winrt::resume_background();
|
||||
localConnection.Close();
|
||||
// connection is destroyed.
|
||||
}
|
||||
}
|
||||
|
||||
void TermControl::Close()
|
||||
{
|
||||
if (!_closing.exchange(true))
|
||||
@@ -1887,11 +2001,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
TSFInputControl().Close(); // Disconnect the TSF input control so it doesn't receive EditContext events.
|
||||
_autoScrollTimer.Stop();
|
||||
|
||||
if (auto localConnection{ std::exchange(_connection, nullptr) })
|
||||
{
|
||||
localConnection.Close();
|
||||
// connection is destroyed.
|
||||
}
|
||||
// GH#1996 - Close the connection asynchronously on a background
|
||||
// thread.
|
||||
// Since TermControl::Close is only ever triggered by the UI, we
|
||||
// don't really care to wait for the connection to be completely
|
||||
// closed. We can just do it whenever.
|
||||
_AsyncCloseConnection();
|
||||
|
||||
if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) })
|
||||
{
|
||||
@@ -1971,7 +2086,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// TODO: MSFT:21254947 - use a static function to do this instead of
|
||||
// instantiating a DxEngine
|
||||
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
THROW_IF_FAILED(dxEngine->UpdateDpi(dpi));
|
||||
THROW_IF_FAILED(dxEngine->UpdateDpi(gsl::narrow_cast<float>(dpi)));
|
||||
THROW_IF_FAILED(dxEngine->UpdateFont(desiredFont, actualFont));
|
||||
|
||||
const float scale = dxEngine->GetScaling();
|
||||
@@ -2243,8 +2358,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
const COORD cursorPos = _terminal->GetCursorPosition();
|
||||
Windows::Foundation::Point p = { gsl::narrow_cast<float>(cursorPos.X), gsl::narrow_cast<float>(cursorPos.Y) };
|
||||
const til::point cursorPos = _terminal->GetCursorPosition();
|
||||
Windows::Foundation::Point p = { ::base::ClampedNumeric<float>(cursorPos.x()), ::base::ClampedNumeric<float>(cursorPos.y()) };
|
||||
eventArgs.CurrentPosition(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
bool OnF7Pressed();
|
||||
|
||||
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta);
|
||||
|
||||
~TermControl();
|
||||
|
||||
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
|
||||
@@ -196,10 +198,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _DoResize(const double newWidth, const double newHeight);
|
||||
void _TerminalTitleChanged(const std::wstring_view& wstr);
|
||||
winrt::fire_and_forget _TerminalScrollPositionChanged(const int viewTop, const int viewHeight, const int bufferSize);
|
||||
winrt::fire_and_forget _TerminalCursorPositionChanged();
|
||||
|
||||
void _MouseScrollHandler(const double delta, Windows::UI::Input::PointerPoint const& pointerPoint);
|
||||
void _MouseScrollHandler(const double mouseDelta, const Windows::Foundation::Point point, const bool isLeftButtonPressed);
|
||||
void _MouseZoomHandler(const double delta);
|
||||
void _MouseTransparencyHandler(const double delta);
|
||||
bool _DoMouseWheel(const Windows::Foundation::Point point, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta, const bool isLeftButtonPressed);
|
||||
|
||||
bool _CapturePointer(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
bool _ReleasePointerCapture(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
@@ -227,6 +231,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _CompositionCompleted(winrt::hstring text);
|
||||
void _CurrentCursorPositionHandler(const IInspectable& sender, const CursorPositionEventArgs& eventArgs);
|
||||
void _FontInfoHandler(const IInspectable& sender, const FontInfoEventArgs& eventArgs);
|
||||
|
||||
winrt::fire_and_forget _AsyncCloseConnection();
|
||||
|
||||
// this atomic is to be used as a guard against dispatching billions of coroutines for
|
||||
// routine state changes that might happen millions of times a second.
|
||||
// Unbounded main dispatcher use leads to massive memory leaks and intense slowdowns
|
||||
// on the UI thread.
|
||||
std::atomic<bool> _coroutineDispatchStateUpdateInProgress{ false };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "IMouseWheelListener.idl";
|
||||
|
||||
namespace Microsoft.Terminal.TerminalControl
|
||||
{
|
||||
delegate void TitleChangedEventArgs(String newTitle);
|
||||
@@ -30,7 +32,7 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
void HandleClipboardData(String data);
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl, IF7Listener
|
||||
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl, IF7Listener, IMouseWheelListener
|
||||
{
|
||||
TermControl();
|
||||
TermControl(Microsoft.Terminal.Settings.IControlSettings settings, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
<DependentUpon>TermControl.xaml</DependentUpon>
|
||||
</Midl>
|
||||
<Midl Include="TermControlAutomationPeer.idl" />
|
||||
<Midl Include="IMouseWheelListener.idl" />
|
||||
<Midl Include="TSFInputControl.idl">
|
||||
<DependentUpon>TSFInputControl.xaml</DependentUpon>
|
||||
</Midl>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#define BLOCK_TIL
|
||||
#include <LibraryIncludes.h>
|
||||
// This is inexplicable, but for whatever reason, cppwinrt conflicts with the
|
||||
// SDK definition of this function, so the only fix is to undef it.
|
||||
@@ -23,6 +25,7 @@
|
||||
#include <unknwn.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.system.h>
|
||||
#include <winrt/Windows.Graphics.Display.h>
|
||||
#include <winrt/windows.ui.core.h>
|
||||
#include <winrt/Windows.ui.input.h>
|
||||
@@ -46,3 +49,5 @@
|
||||
#include <TraceLoggingProvider.h>
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalControlProvider);
|
||||
#include <telemetry/ProjectTelemetry.h>
|
||||
|
||||
#include "til.h"
|
||||
|
||||
@@ -47,6 +47,27 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef WINRT_Windows_System_H
|
||||
ControlKeyStates(const winrt::Windows::System::VirtualKeyModifiers& modifiers) noexcept :
|
||||
_value{ 0 }
|
||||
{
|
||||
// static_cast to a uint32_t because we can't use the WI_IsFlagSet
|
||||
// macro directly with a VirtualKeyModifiers
|
||||
const auto m = static_cast<uint32_t>(modifiers);
|
||||
_value |= WI_IsFlagSet(m, static_cast<uint32_t>(winrt::Windows::System::VirtualKeyModifiers::Shift)) ?
|
||||
SHIFT_PRESSED :
|
||||
0;
|
||||
|
||||
// Since we can't differentiate between the left & right versions of Ctrl & Alt in a VirtualKeyModifiers
|
||||
_value |= WI_IsFlagSet(m, static_cast<uint32_t>(winrt::Windows::System::VirtualKeyModifiers::Menu)) ?
|
||||
LEFT_ALT_PRESSED :
|
||||
0;
|
||||
_value |= WI_IsFlagSet(m, static_cast<uint32_t>(winrt::Windows::System::VirtualKeyModifiers::Control)) ?
|
||||
LEFT_CTRL_PRESSED :
|
||||
0;
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr DWORD Value() const noexcept
|
||||
{
|
||||
return _value;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Microsoft::Terminal::Core
|
||||
|
||||
virtual bool SendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states) = 0;
|
||||
virtual bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta) = 0;
|
||||
virtual bool SendCharEvent(const wchar_t ch) = 0;
|
||||
virtual bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) = 0;
|
||||
|
||||
// void SendMouseEvent(uint row, uint col, KeyModifiers modifiers);
|
||||
[[nodiscard]] virtual HRESULT UserResize(const COORD size) noexcept = 0;
|
||||
|
||||
@@ -387,14 +387,21 @@ bool Terminal::IsTrackingMouseInput() const noexcept
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Send this particular key event to the terminal. The terminal will translate
|
||||
// the key and the modifiers pressed into the appropriate VT sequence for that
|
||||
// key chord. If we do translate the key, we'll return true. In that case, the
|
||||
// event should NOT be processed any further. If we return false, the event
|
||||
// was NOT translated, and we should instead use the event to try and get the
|
||||
// real character out of the event.
|
||||
// - Send this particular (non-character) key event to the terminal.
|
||||
// - The terminal will translate the key and the modifiers pressed into the
|
||||
// appropriate VT sequence for that key chord. If we do translate the key,
|
||||
// we'll return true. In that case, the event should NOT be processed any further.
|
||||
// - Character events (e.g. WM_CHAR) are generally the best way to properly receive
|
||||
// keyboard input on Windows though, as the OS is suited best at handling the
|
||||
// translation of the current keyboard layout, dead keys, etc.
|
||||
// As a result of this false is returned for all key events that contain characters.
|
||||
// SendCharEvent may then be called with the data obtained from a character event.
|
||||
// - As a special case we'll always handle VK_TAB key events.
|
||||
// This must be done due to TermControl::_KeyDownHandler (one of the callers)
|
||||
// always marking tab key events as handled, causing no character event to be raised.
|
||||
// Arguments:
|
||||
// - vkey: The vkey of the key pressed.
|
||||
// - vkey: The vkey of the last pressed key.
|
||||
// - scanCode: The scan code of the last pressed key.
|
||||
// - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
|
||||
// Return Value:
|
||||
// - true if we translated the key event, and it should not be processed any further.
|
||||
@@ -402,50 +409,35 @@ bool Terminal::IsTrackingMouseInput() const noexcept
|
||||
bool Terminal::SendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states)
|
||||
{
|
||||
TrySnapOnInput();
|
||||
_StoreKeyEvent(vkey, scanCode);
|
||||
|
||||
const auto isAltOnlyPressed = states.IsAltPressed() && !states.IsCtrlPressed();
|
||||
|
||||
// Alt key sequences _require_ the char to be in the keyevent. If alt is
|
||||
// pressed, manually get the character that's being typed, and put it in the
|
||||
// KeyEvent.
|
||||
// DON'T manually handle Alt+Space - the system will use this to bring up
|
||||
// the system menu for restore, min/maximize, size, move, close
|
||||
wchar_t ch = UNICODE_NULL;
|
||||
if (states.IsAltPressed() && vkey != VK_SPACE)
|
||||
// the system menu for restore, min/maximize, size, move, close.
|
||||
// (This doesn't apply to Ctrl+Alt+Space.)
|
||||
if (isAltOnlyPressed && vkey == VK_SPACE)
|
||||
{
|
||||
ch = _CharacterFromKeyEvent(vkey, scanCode, states);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (states.IsCtrlPressed())
|
||||
{
|
||||
switch (vkey)
|
||||
{
|
||||
case 0x48:
|
||||
// Manually handle Ctrl+H. Ctrl+H should be handled as Backspace. To do this
|
||||
// correctly, the keyEvents's char needs to be set to Backspace.
|
||||
// 0x48 is the VKEY for 'H', which isn't named
|
||||
ch = UNICODE_BACKSPACE;
|
||||
break;
|
||||
case VK_SPACE:
|
||||
// Manually handle Ctrl+Space here. The terminalInput translator requires
|
||||
// the char to be set to Space for space handling to work correctly.
|
||||
ch = UNICODE_SPACE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto ch = _CharacterFromKeyEvent(vkey, scanCode, states);
|
||||
|
||||
// Manually handle Escape here. If we let it fall through, it'll come
|
||||
// back up through the character handler. It's registered as a translation
|
||||
// in TerminalInput, so we'll let TerminalInput control it.
|
||||
if (vkey == VK_ESCAPE)
|
||||
// Delegate it to the character event handler if this key event can be
|
||||
// mapped to one (see method description above). For Alt+key combinations
|
||||
// we'll not receive another character event for some reason though.
|
||||
// -> Don't delegate the event if this is a Alt+key combination.
|
||||
//
|
||||
// As a special case we'll furthermore always handle VK_TAB
|
||||
// key events here instead of in Terminal::SendCharEvent.
|
||||
// See the method description for more information.
|
||||
if (!isAltOnlyPressed && vkey != VK_TAB && ch != UNICODE_NULL)
|
||||
{
|
||||
ch = UNICODE_ESC;
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool manuallyHandled = ch != UNICODE_NULL;
|
||||
|
||||
KeyEvent keyEv{ true, 0, vkey, scanCode, ch, states.Value() };
|
||||
const bool translated = _terminalInput->HandleKey(&keyEv);
|
||||
|
||||
return translated && manuallyHandled;
|
||||
return _terminalInput->HandleKey(&keyEv);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -474,9 +466,39 @@ bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButt
|
||||
return _terminalInput->HandleMouse(viewportPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta);
|
||||
}
|
||||
|
||||
bool Terminal::SendCharEvent(const wchar_t ch)
|
||||
// Method Description:
|
||||
// - Send this particular character to the terminal.
|
||||
// - This method is the counterpart to SendKeyEvent and behaves almost identical.
|
||||
// The difference is the focus on sending characters to the terminal,
|
||||
// whereas SendKeyEvent handles the sending of keys like the arrow keys.
|
||||
// Arguments:
|
||||
// - ch: The UTF-16 code unit to be sent.
|
||||
// - scanCode: The scan code of the last pressed key. Can be left 0.
|
||||
// - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
|
||||
// Return Value:
|
||||
// - true if we translated the character event, and it should not be processed any further.
|
||||
// - false otherwise.
|
||||
bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states)
|
||||
{
|
||||
return _terminalInput->HandleChar(ch);
|
||||
// DON'T manually handle Alt+Space - the system will use this to bring up
|
||||
// the system menu for restore, min/maximize, size, move, close.
|
||||
if (ch == L' ' && states.IsAltPressed() && !states.IsCtrlPressed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto vkey = _TakeVirtualKeyFromLastKeyEvent(scanCode);
|
||||
if (vkey == 0 && scanCode != 0)
|
||||
{
|
||||
vkey = _VirtualKeyFromScanCode(scanCode);
|
||||
}
|
||||
if (vkey == 0)
|
||||
{
|
||||
vkey = _VirtualKeyFromCharacter(ch);
|
||||
}
|
||||
|
||||
KeyEvent keyEv{ true, 0, vkey, scanCode, ch, states.Value() };
|
||||
return _terminalInput->HandleKey(&keyEv);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -490,6 +512,29 @@ WORD Terminal::_ScanCodeFromVirtualKey(const WORD vkey) noexcept
|
||||
return LOWORD(MapVirtualKeyW(vkey, MAPVK_VK_TO_VSC));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns the virtual key code for the given keyboard's scan code.
|
||||
// Arguments:
|
||||
// - scanCode: The keyboard's scan code.
|
||||
// Return Value:
|
||||
// - The virtual key code. 0 if no mapping can be found.
|
||||
WORD Terminal::_VirtualKeyFromScanCode(const WORD scanCode) noexcept
|
||||
{
|
||||
return LOWORD(MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns any virtual key code that produces the given character.
|
||||
// Arguments:
|
||||
// - scanCode: The keyboard's scan code.
|
||||
// Return Value:
|
||||
// - The virtual key code. 0 if no mapping can be found.
|
||||
WORD Terminal::_VirtualKeyFromCharacter(const wchar_t ch) noexcept
|
||||
{
|
||||
const auto vkey = LOWORD(VkKeyScanW(ch));
|
||||
return vkey == -1 ? 0 : vkey;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Translates the specified virtual key code and keyboard state to the corresponding character.
|
||||
// Arguments:
|
||||
@@ -532,6 +577,40 @@ catch (...)
|
||||
return UNICODE_INVALID;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - It's possible for a single scan code on a keyboard to
|
||||
// produce different key codes depending on the keyboard state.
|
||||
// MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK) will always chose one of the
|
||||
// possibilities no matter what though and thus can't be used in SendCharEvent.
|
||||
// - This method stores the key code from a key event (SendKeyEvent).
|
||||
// If the key event contains character data, handling of the event will be
|
||||
// denied, in order to delegate the work to the character event handler.
|
||||
// - The character event handler (SendCharEvent) will now pick up
|
||||
// the stored key code to restore the full key event data.
|
||||
// Arguments:
|
||||
// - vkey: The virtual key code.
|
||||
// - scanCode: The scan code.
|
||||
void Terminal::_StoreKeyEvent(const WORD vkey, const WORD scanCode)
|
||||
{
|
||||
_lastKeyEventCodes.emplace(KeyEventCodes{ vkey, scanCode });
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method acts as a counterpart to _StoreKeyEvent and extracts a stored
|
||||
// key code. As a safety measure it'll ensure that the given scan code
|
||||
// matches the stored scan code from the previous key event.
|
||||
// - See _StoreKeyEvent for more information.
|
||||
// Arguments:
|
||||
// - scanCode: The scan code.
|
||||
// Return Value:
|
||||
// - The key code matching the given scan code. Otherwise 0.
|
||||
WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept
|
||||
{
|
||||
const auto codes = _lastKeyEventCodes.value_or(KeyEventCodes{});
|
||||
_lastKeyEventCodes.reset();
|
||||
return codes.ScanCode == scanCode ? codes.VirtualKey : 0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Acquire a read lock on the terminal.
|
||||
// Return Value:
|
||||
@@ -704,6 +783,8 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
|
||||
_buffer->GetRenderTarget().TriggerRedrawAll();
|
||||
_NotifyScrollEvent();
|
||||
}
|
||||
|
||||
_NotifyTerminalCursorPositionChanged();
|
||||
}
|
||||
|
||||
void Terminal::UserScrollViewport(const int viewTop)
|
||||
@@ -736,6 +817,14 @@ try
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
void Terminal::_NotifyTerminalCursorPositionChanged() noexcept
|
||||
{
|
||||
if (_pfnCursorPositionChanged)
|
||||
{
|
||||
_pfnCursorPositionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Terminal::SetWriteInputCallback(std::function<void(std::wstring&)> pfn) noexcept
|
||||
{
|
||||
_pfnWriteInput.swap(pfn);
|
||||
@@ -751,6 +840,11 @@ void Terminal::SetScrollPositionChangedCallback(std::function<void(const int, co
|
||||
_pfnScrollPositionChanged.swap(pfn);
|
||||
}
|
||||
|
||||
void Terminal::SetCursorPositionChangedCallback(std::function<void()> pfn) noexcept
|
||||
{
|
||||
_pfnCursorPositionChanged.swap(pfn);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Allows setting a callback for when the background color is changed
|
||||
// Arguments:
|
||||
|
||||
@@ -115,7 +115,7 @@ public:
|
||||
// These methods are defined in Terminal.cpp
|
||||
bool SendKeyEvent(const WORD vkey, const WORD scanCode, const Microsoft::Terminal::Core::ControlKeyStates states) override;
|
||||
bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta) override;
|
||||
bool SendCharEvent(const wchar_t ch) override;
|
||||
bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) override;
|
||||
|
||||
[[nodiscard]] HRESULT UserResize(const COORD viewportSize) noexcept override;
|
||||
void UserScrollViewport(const int viewTop) override;
|
||||
@@ -168,6 +168,7 @@ public:
|
||||
void SetWriteInputCallback(std::function<void(std::wstring&)> pfn) noexcept;
|
||||
void SetTitleChangedCallback(std::function<void(const std::wstring_view&)> pfn) noexcept;
|
||||
void SetScrollPositionChangedCallback(std::function<void(const int, const int, const int)> pfn) noexcept;
|
||||
void SetCursorPositionChangedCallback(std::function<void()> pfn) noexcept;
|
||||
void SetBackgroundCallback(std::function<void(const uint32_t)> pfn) noexcept;
|
||||
|
||||
void SetCursorOn(const bool isOn) noexcept;
|
||||
@@ -194,6 +195,7 @@ private:
|
||||
std::function<void(const std::wstring_view&)> _pfnTitleChanged;
|
||||
std::function<void(const int, const int, const int)> _pfnScrollPositionChanged;
|
||||
std::function<void(const uint32_t)> _pfnBackgroundColorChanged;
|
||||
std::function<void()> _pfnCursorPositionChanged;
|
||||
|
||||
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
|
||||
std::unique_ptr<::Microsoft::Console::VirtualTerminal::TerminalInput> _terminalInput;
|
||||
@@ -249,9 +251,22 @@ private:
|
||||
// underneath them, while others would prefer to anchor it in place.
|
||||
// Either way, we should make this behavior controlled by a setting.
|
||||
|
||||
// Since virtual keys are non-zero, you assume that this field is empty/invalid if it is.
|
||||
struct KeyEventCodes
|
||||
{
|
||||
WORD VirtualKey;
|
||||
WORD ScanCode;
|
||||
};
|
||||
std::optional<KeyEventCodes> _lastKeyEventCodes;
|
||||
|
||||
static WORD _ScanCodeFromVirtualKey(const WORD vkey) noexcept;
|
||||
static WORD _VirtualKeyFromScanCode(const WORD scanCode) noexcept;
|
||||
static WORD _VirtualKeyFromCharacter(const wchar_t ch) noexcept;
|
||||
static wchar_t _CharacterFromKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states) noexcept;
|
||||
|
||||
void _StoreKeyEvent(const WORD vkey, const WORD scanCode);
|
||||
WORD _TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept;
|
||||
|
||||
int _VisibleStartIndex() const noexcept;
|
||||
int _VisibleEndIndex() const noexcept;
|
||||
|
||||
@@ -266,6 +281,8 @@ private:
|
||||
|
||||
void _NotifyScrollEvent() noexcept;
|
||||
|
||||
void _NotifyTerminalCursorPositionChanged() noexcept;
|
||||
|
||||
#pragma region TextSelection
|
||||
// These methods are defined in TerminalSelection.cpp
|
||||
std::vector<SMALL_RECT> _GetSelectionRects() const noexcept;
|
||||
|
||||
@@ -221,18 +221,18 @@ void Terminal::ClearSelection()
|
||||
// Method Description:
|
||||
// - get wstring text from highlighted portion of text buffer
|
||||
// Arguments:
|
||||
// - collapseText: collapse all of the text to one line
|
||||
// - singleLine: collapse all of the text to one line
|
||||
// Return Value:
|
||||
// - wstring text from buffer. If extended to multiple lines, each line is separated by \r\n
|
||||
const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool collapseText) const
|
||||
const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool singleLine) const
|
||||
{
|
||||
const auto selectionRects = _GetSelectionRects();
|
||||
|
||||
std::function<COLORREF(TextAttribute&)> GetForegroundColor = std::bind(&Terminal::GetForegroundColor, this, std::placeholders::_1);
|
||||
std::function<COLORREF(TextAttribute&)> GetBackgroundColor = std::bind(&Terminal::GetBackgroundColor, this, std::placeholders::_1);
|
||||
|
||||
return _buffer->GetText(!collapseText,
|
||||
!collapseText,
|
||||
return _buffer->GetText(!singleLine,
|
||||
!singleLine,
|
||||
selectionRects,
|
||||
GetForegroundColor,
|
||||
GetBackgroundColor);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
class InputBuffer; // This for some reason needs to be fwd-decl'd
|
||||
#include "../host/inputBuffer.hpp"
|
||||
#include "../host/readDataCooked.hpp"
|
||||
#include "../host/output.h"
|
||||
#include "test/CommonState.hpp"
|
||||
|
||||
#include "../cascadia/TerminalCore/Terminal.hpp"
|
||||
@@ -50,6 +51,9 @@ using namespace TerminalCoreUnitTests;
|
||||
|
||||
class TerminalCoreUnitTests::ConptyRoundtripTests final
|
||||
{
|
||||
// !!! DANGER: Many tests in this class expect the Terminal and Host buffers
|
||||
// to be 80x32. If you change these, you'll probably inadvertently break a
|
||||
// bunch of tests !!!
|
||||
static const SHORT TerminalViewWidth = 80;
|
||||
static const SHORT TerminalViewHeight = 32;
|
||||
|
||||
@@ -174,6 +178,16 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
|
||||
|
||||
TEST_METHOD(TestResizeHeight);
|
||||
|
||||
TEST_METHOD(OutputWrappedLinesAtTopOfBuffer);
|
||||
TEST_METHOD(OutputWrappedLinesAtBottomOfBuffer);
|
||||
TEST_METHOD(ScrollWithChangesInMiddle);
|
||||
TEST_METHOD(DontWrapMoveCursorInSingleFrame);
|
||||
TEST_METHOD(ClearHostTrickeryTest);
|
||||
TEST_METHOD(OverstrikeAtBottomOfBuffer);
|
||||
TEST_METHOD(MarginsWithStatusLine);
|
||||
TEST_METHOD(OutputWrappedLineWithSpace);
|
||||
TEST_METHOD(OutputWrappedLineWithSpaceAtBottomOfBuffer);
|
||||
|
||||
TEST_METHOD(ScrollWithMargins);
|
||||
|
||||
private:
|
||||
@@ -950,7 +964,7 @@ void ConptyRoundtripTests::PassthroughClearScrollback()
|
||||
else
|
||||
{
|
||||
// After we hit the bottom of the viewport, the newlines come in
|
||||
// separated for whatever reason.
|
||||
// separated by empty writes for whatever reason.
|
||||
expectedOutput.push_back("\r");
|
||||
expectedOutput.push_back("\n");
|
||||
expectedOutput.push_back("");
|
||||
@@ -1026,8 +1040,7 @@ void ConptyRoundtripTests::PassthroughHardReset()
|
||||
else
|
||||
{
|
||||
// After we hit the bottom of the viewport, the newlines come in
|
||||
// separated for whatever reason.
|
||||
|
||||
// separated by empty writes for whatever reason.
|
||||
expectedOutput.push_back("\r");
|
||||
expectedOutput.push_back("\n");
|
||||
expectedOutput.push_back("");
|
||||
@@ -1061,6 +1074,293 @@ void ConptyRoundtripTests::PassthroughHardReset()
|
||||
}
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::OutputWrappedLinesAtTopOfBuffer()
|
||||
{
|
||||
Log::Comment(
|
||||
L"Case 1: Write a wrapped line right at the start of the buffer, before any circling");
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
auto& hostTb = si.GetTextBuffer();
|
||||
auto& termTb = *term->_buffer;
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
const auto wrappedLineLength = TerminalViewWidth + 20;
|
||||
|
||||
sm.ProcessString(std::wstring(wrappedLineLength, L'A'));
|
||||
|
||||
auto verifyBuffer = [](const TextBuffer& tb) {
|
||||
// Buffer contents should look like the following: (80 wide)
|
||||
// (w) means we hard wrapped the line
|
||||
// (b) means the line is _not_ wrapped (it's broken, the default state.)
|
||||
// cursor is on the '_'
|
||||
//
|
||||
// |AAAAAAAA...AAAA| (w)
|
||||
// |AAAAA_ ... | (b) (There are 20 'A's on this line.)
|
||||
// | ... | (b)
|
||||
|
||||
VERIFY_IS_TRUE(tb.GetRowByOffset(0).GetCharRow().WasWrapForced());
|
||||
VERIFY_IS_FALSE(tb.GetRowByOffset(1).GetCharRow().WasWrapForced());
|
||||
auto iter0 = tb.GetCellDataAt({ 0, 0 });
|
||||
TestUtils::VerifySpanOfText(L"A", iter0, 0, TerminalViewWidth);
|
||||
auto iter1 = tb.GetCellDataAt({ 0, 1 });
|
||||
TestUtils::VerifySpanOfText(L"A", iter1, 0, 20);
|
||||
auto iter2 = tb.GetCellDataAt({ 20, 1 });
|
||||
TestUtils::VerifySpanOfText(L" ", iter2, 0, TerminalViewWidth - 20);
|
||||
};
|
||||
|
||||
verifyBuffer(hostTb);
|
||||
|
||||
expectedOutput.push_back(std::string(TerminalViewWidth, 'A'));
|
||||
expectedOutput.push_back(std::string(20, 'A'));
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyBuffer(termTb);
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::OutputWrappedLinesAtBottomOfBuffer()
|
||||
{
|
||||
Log::Comment(
|
||||
L"Case 2: Write a wrapped line at the end of the buffer, once the conpty started circling");
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& hostSm = si.GetStateMachine();
|
||||
auto& hostTb = si.GetTextBuffer();
|
||||
auto& termTb = *term->_buffer;
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
// First, fill the buffer with contents, so conpty starts circling
|
||||
|
||||
const auto hostView = si.GetViewport();
|
||||
const auto end = 2 * hostView.Height();
|
||||
for (auto i = 0; i < end; i++)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"Writing line %d/%d", i, end));
|
||||
expectedOutput.push_back("X");
|
||||
if (i < hostView.BottomInclusive())
|
||||
{
|
||||
expectedOutput.push_back("\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// After we hit the bottom of the viewport, the newlines come in
|
||||
// separated by empty writes for whatever reason.
|
||||
expectedOutput.push_back("\r");
|
||||
expectedOutput.push_back("\n");
|
||||
expectedOutput.push_back("");
|
||||
}
|
||||
|
||||
hostSm.ProcessString(L"X\n");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
const auto wrappedLineLength = TerminalViewWidth + 20;
|
||||
|
||||
// The following diagrams show the buffer contents after each string emitted
|
||||
// from conpty. For each of these diagrams:
|
||||
// (w) means we hard wrapped the line
|
||||
// (b) means the line is _not_ wrapped (it's broken, the default state.)
|
||||
// cursor is on the '_'
|
||||
|
||||
// Initial state:
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |_ | (b)
|
||||
|
||||
expectedOutput.push_back(std::string(TerminalViewWidth, 'A'));
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AAAA|_ (w) The cursor is actually on the last A here
|
||||
|
||||
// TODO GH#5228 might break the "newline & repaint the wrapped char" checks here, that's okay.
|
||||
expectedOutput.push_back("\r"); // This \r\n is emitted by ScrollFrame to
|
||||
expectedOutput.push_back("\n"); // add a newline to the bottom of the buffer
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AAAA| (b)
|
||||
// |_ | (b)
|
||||
|
||||
expectedOutput.push_back("\x1b[31;80H"); // Move the cursor BACK to the wrapped row
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AAAA| (b) The cursor is actually on the last A here
|
||||
// | | (b)
|
||||
|
||||
expectedOutput.push_back(std::string(1, 'A')); // Reprint the last character of the wrapped row
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AAAA|_ (w) The cursor is actually on the last A here
|
||||
// | | (b)
|
||||
|
||||
expectedOutput.push_back(std::string(20, 'A')); // Print the second line.
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AAAA| (w)
|
||||
// |AAAAA_ | (b) There are 20 'A's on this line.
|
||||
|
||||
hostSm.ProcessString(std::wstring(wrappedLineLength, L'A'));
|
||||
|
||||
auto verifyBuffer = [](const TextBuffer& tb, const short wrappedRow) {
|
||||
// Buffer contents should look like the following: (80 wide)
|
||||
// (w) means we hard wrapped the line
|
||||
// (b) means the line is _not_ wrapped (it's broken, the default state.)
|
||||
// cursor is on the '_'
|
||||
//
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AAAA| (w)
|
||||
// |AAAAA_ ... | (b) (There are 20 'A's on this line.)
|
||||
|
||||
VERIFY_IS_TRUE(tb.GetRowByOffset(wrappedRow).GetCharRow().WasWrapForced());
|
||||
VERIFY_IS_FALSE(tb.GetRowByOffset(wrappedRow + 1).GetCharRow().WasWrapForced());
|
||||
|
||||
auto iter0 = tb.GetCellDataAt({ 0, wrappedRow });
|
||||
TestUtils::VerifySpanOfText(L"A", iter0, 0, TerminalViewWidth);
|
||||
auto iter1 = tb.GetCellDataAt({ 0, wrappedRow + 1 });
|
||||
TestUtils::VerifySpanOfText(L"A", iter1, 0, 20);
|
||||
auto iter2 = tb.GetCellDataAt({ 20, wrappedRow + 1 });
|
||||
TestUtils::VerifySpanOfText(L" ", iter2, 0, TerminalViewWidth - 20);
|
||||
};
|
||||
|
||||
verifyBuffer(hostTb, hostView.BottomInclusive() - 1);
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyBuffer(termTb, term->_mutableViewport.BottomInclusive() - 1);
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::ScrollWithChangesInMiddle()
|
||||
{
|
||||
Log::Comment(L"This test checks emitting a wrapped line at the bottom of the"
|
||||
L" viewport while _also_ emitting other text elsewhere in the same frame. This"
|
||||
L" output will cause us to scroll the viewport in one frame, but we need to"
|
||||
L" make sure the wrapped line _stays_ wrapped, and the scrolled text appears in"
|
||||
L" the right place.");
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& hostSm = si.GetStateMachine();
|
||||
auto& hostTb = si.GetTextBuffer();
|
||||
auto& termTb = *term->_buffer;
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
// First, fill the buffer with contents, so conpty starts circling
|
||||
|
||||
const auto hostView = si.GetViewport();
|
||||
const auto end = 2 * hostView.Height();
|
||||
for (auto i = 0; i < end; i++)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"Writing line %d/%d", i, end));
|
||||
expectedOutput.push_back("X");
|
||||
if (i < hostView.BottomInclusive())
|
||||
{
|
||||
expectedOutput.push_back("\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// After we hit the bottom of the viewport, the newlines come in
|
||||
// separated by empty writes for whatever reason.
|
||||
expectedOutput.push_back("\r");
|
||||
expectedOutput.push_back("\n");
|
||||
expectedOutput.push_back("");
|
||||
}
|
||||
|
||||
hostSm.ProcessString(L"X\n");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
const auto wrappedLineLength = TerminalViewWidth + 20;
|
||||
|
||||
// In the Terminal, we're going to expect:
|
||||
expectedOutput.push_back("\x1b[15;1H"); // Move the cursor to row 14, col 0
|
||||
expectedOutput.push_back("Y"); // Print a 'Y'
|
||||
expectedOutput.push_back("\x1b[32;1H"); // Move the cursor to the last row
|
||||
expectedOutput.push_back(std::string(TerminalViewWidth, 'A')); // Print the first 80 'A's
|
||||
// This is going to be the end of the first frame - b/c we moved the cursor
|
||||
// in the middle of the frame, we're going to hide/show the cursor during
|
||||
// this frame
|
||||
expectedOutput.push_back("\x1b[?25h"); // hide the cursor
|
||||
// On the subsequent frame:
|
||||
// TODO GH#5228 might break the "newline & repaint the wrapped char" checks here, that's okay.
|
||||
expectedOutput.push_back("\r"); // This \r\n is emitted by ScrollFrame to
|
||||
expectedOutput.push_back("\n"); // add a newline to the bottom of the buffer
|
||||
expectedOutput.push_back("\x1b[31;80H"); // Move the cursor BACK to the wrapped row
|
||||
expectedOutput.push_back(std::string(1, 'A')); // Reprint the last character of the wrapped row
|
||||
expectedOutput.push_back(std::string(20, 'A')); // Print the second line.
|
||||
|
||||
_logConpty = true;
|
||||
|
||||
// To the host, we'll do something very similar:
|
||||
hostSm.ProcessString(L"\x1b"
|
||||
L"7"); // Save cursor
|
||||
hostSm.ProcessString(L"\x1b[15;1H"); // Move the cursor to row 14, col 0
|
||||
hostSm.ProcessString(L"Y"); // Print a 'Y'
|
||||
hostSm.ProcessString(L"\x1b"
|
||||
L"8"); // Restore
|
||||
hostSm.ProcessString(std::wstring(wrappedLineLength, L'A')); // Print 100 'A's
|
||||
|
||||
auto verifyBuffer = [](const TextBuffer& tb, const til::rectangle viewport) {
|
||||
const short wrappedRow = viewport.bottom<short>() - 2;
|
||||
const short start = viewport.top<short>();
|
||||
for (short i = start; i < wrappedRow; i++)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"Checking row %d", i));
|
||||
TestUtils::VerifyExpectedString(tb, i == start + 13 ? L"Y" : L"X", { 0, i });
|
||||
}
|
||||
|
||||
VERIFY_IS_TRUE(tb.GetRowByOffset(wrappedRow).GetCharRow().WasWrapForced());
|
||||
VERIFY_IS_FALSE(tb.GetRowByOffset(wrappedRow + 1).GetCharRow().WasWrapForced());
|
||||
|
||||
auto iter0 = tb.GetCellDataAt({ 0, wrappedRow });
|
||||
TestUtils::VerifySpanOfText(L"A", iter0, 0, TerminalViewWidth);
|
||||
|
||||
auto iter1 = tb.GetCellDataAt({ 0, wrappedRow + 1 });
|
||||
TestUtils::VerifySpanOfText(L"A", iter1, 0, 20);
|
||||
auto iter2 = tb.GetCellDataAt({ 20, wrappedRow + 1 });
|
||||
TestUtils::VerifySpanOfText(L" ", iter2, 0, TerminalViewWidth - 20);
|
||||
};
|
||||
|
||||
Log::Comment(NoThrowString().Format(L"Checking the host buffer..."));
|
||||
verifyBuffer(hostTb, hostView.ToInclusive());
|
||||
Log::Comment(NoThrowString().Format(L"... Done"));
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(NoThrowString().Format(L"Checking the terminal buffer..."));
|
||||
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
|
||||
Log::Comment(NoThrowString().Format(L"... Done"));
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::ScrollWithMargins()
|
||||
{
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
@@ -1087,6 +1387,7 @@ void ConptyRoundtripTests::ScrollWithMargins()
|
||||
// The letters represent the data in the TMUX pane.
|
||||
// The final *** line represents the mode line which we will
|
||||
// attempt to hold in place and not scroll.
|
||||
// Note that the last line will contain one '*' less than the width of the window.
|
||||
|
||||
Log::Comment(L"Fill host with text pattern by feeding it into VT parser.");
|
||||
const auto rowsToWrite = initialTermView.Height() - 1;
|
||||
@@ -1103,7 +1404,7 @@ void ConptyRoundtripTests::ScrollWithMargins()
|
||||
}
|
||||
|
||||
// For the last one, write out the asterisks for the mode line.
|
||||
for (auto i = 0; i < initialTermView.Width(); ++i)
|
||||
for (auto i = 0; i < initialTermView.Width() - 1; ++i)
|
||||
{
|
||||
hostSm.ProcessCharacter('*');
|
||||
}
|
||||
@@ -1127,7 +1428,7 @@ void ConptyRoundtripTests::ScrollWithMargins()
|
||||
}
|
||||
|
||||
// For the last row, verify we have an entire row of asterisks for the mode line.
|
||||
const std::wstring expectedModeLine(initialTermView.Width(), L'*');
|
||||
const std::wstring expectedModeLine(initialTermView.Width() - 1, L'*');
|
||||
const COORD expectedPos{ 0, gsl::narrow<SHORT>(rowsToWrite) };
|
||||
TestUtils::VerifyExpectedString(tb, expectedModeLine, expectedPos);
|
||||
};
|
||||
@@ -1140,13 +1441,8 @@ void ConptyRoundtripTests::ScrollWithMargins()
|
||||
expectedOutput.push_back("\r\n");
|
||||
}
|
||||
{
|
||||
const std::string expectedString(initialTermView.Width(), '*');
|
||||
const std::string expectedString(initialTermView.Width() - 1, '*');
|
||||
expectedOutput.push_back(expectedString);
|
||||
|
||||
// Cursor gets reset into bottom right corner as we're writing all the way into that corner.
|
||||
std::stringstream ss;
|
||||
ss << "\x1b[" << initialTermView.Height() << ";" << initialTermView.Width() << "H";
|
||||
expectedOutput.push_back(ss.str());
|
||||
}
|
||||
|
||||
Log::Comment(L"Verify host buffer contains pattern.");
|
||||
@@ -1257,7 +1553,7 @@ void ConptyRoundtripTests::ScrollWithMargins()
|
||||
|
||||
// For the last row, verify we have an entire row of asterisks for the mode line.
|
||||
{
|
||||
const std::wstring expectedModeLine(initialTermView.Width(), L'*');
|
||||
const std::wstring expectedModeLine(initialTermView.Width() - 1, L'*');
|
||||
const COORD modeLinePos{ 0, gsl::narrow<SHORT>(rowsToWrite) };
|
||||
TestUtils::VerifyExpectedString(tb, expectedModeLine, modeLinePos);
|
||||
}
|
||||
@@ -1279,8 +1575,9 @@ void ConptyRoundtripTests::ScrollWithMargins()
|
||||
expectedOutput.push_back("\r\n");
|
||||
}
|
||||
{
|
||||
const std::string expectedString(initialTermView.Width(), '*');
|
||||
expectedOutput.push_back(expectedString);
|
||||
const std::string expectedString(initialTermView.Width() - 1, '*');
|
||||
// There will be one extra blank space at the end of the line, to prevent delayed EOL wrapping
|
||||
expectedOutput.push_back(expectedString + " ");
|
||||
}
|
||||
{
|
||||
// Cursor gets reset into second line from bottom, left most column
|
||||
@@ -1302,3 +1599,590 @@ void ConptyRoundtripTests::ScrollWithMargins()
|
||||
// Verify the terminal side.
|
||||
verifyBufferAfter(termTb);
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::DontWrapMoveCursorInSingleFrame()
|
||||
{
|
||||
// See https://github.com/microsoft/terminal/pull/5181#issuecomment-607427840
|
||||
Log::Comment(L"This is a test for when a line of text exactly wrapped, but "
|
||||
L"the cursor didn't end the frame at the end of line (waiting "
|
||||
L"for more wrapped text). We should still move the cursor in "
|
||||
L"this case.");
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& hostSm = si.GetStateMachine();
|
||||
auto& hostTb = si.GetTextBuffer();
|
||||
auto& termTb = *term->_buffer;
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
auto verifyBuffer = [](const TextBuffer& tb) {
|
||||
// Simple verification: Make sure the cursor is in the correct place,
|
||||
// and that it's visible. We don't care so much about the buffer
|
||||
// contents in this test.
|
||||
const COORD expectedCursor{ 8, 3 };
|
||||
VERIFY_ARE_EQUAL(expectedCursor, tb.GetCursor().GetPosition());
|
||||
VERIFY_IS_TRUE(tb.GetCursor().IsVisible());
|
||||
};
|
||||
|
||||
hostSm.ProcessString(L"\x1b[?25l");
|
||||
hostSm.ProcessString(L"\x1b[H");
|
||||
hostSm.ProcessString(L"\x1b[75C");
|
||||
hostSm.ProcessString(L"XXXXX");
|
||||
hostSm.ProcessString(L"\x1b[4;9H");
|
||||
hostSm.ProcessString(L"\x1b[?25h");
|
||||
|
||||
Log::Comment(L"Checking the host buffer state");
|
||||
verifyBuffer(hostTb);
|
||||
|
||||
expectedOutput.push_back("\x1b[75C");
|
||||
expectedOutput.push_back("XXXXX");
|
||||
expectedOutput.push_back("\x1b[4;9H");
|
||||
// We're _not_ expecting a cursor on here, because we didn't actually hide
|
||||
// the cursor during the course of this frame
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"Checking the terminal buffer state");
|
||||
verifyBuffer(termTb);
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::ClearHostTrickeryTest()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:paintEachNewline", L"{0, 1, 2}")
|
||||
TEST_METHOD_PROPERTY(L"Data:cursorOnNextLine", L"{false, true}")
|
||||
TEST_METHOD_PROPERTY(L"Data:paintAfterDECALN", L"{false, true}")
|
||||
TEST_METHOD_PROPERTY(L"Data:changeAttributes", L"{false, true}")
|
||||
TEST_METHOD_PROPERTY(L"Data:useLongSpaces", L"{false, true}")
|
||||
TEST_METHOD_PROPERTY(L"Data:printTextAfterSpaces", L"{false, true}")
|
||||
END_TEST_METHOD_PROPERTIES();
|
||||
constexpr int PaintEveryNewline = 0;
|
||||
constexpr int PaintAfterAllNewlines = 1;
|
||||
constexpr int DontPaintAfterNewlines = 2;
|
||||
|
||||
INIT_TEST_PROPERTY(int, paintEachNewline, L"Any of: manually PaintFrame after each newline is emitted, once at the end of all newlines, or not at all");
|
||||
INIT_TEST_PROPERTY(bool, cursorOnNextLine, L"Either leave the cursor on the first line, or place it on the second line of the buffer");
|
||||
INIT_TEST_PROPERTY(bool, paintAfterDECALN, L"Controls whether we manually paint a frame after the DECALN sequence is emitted.");
|
||||
INIT_TEST_PROPERTY(bool, changeAttributes, L"If true, change the text attributes after the 'A's and spaces");
|
||||
INIT_TEST_PROPERTY(bool, useLongSpaces, L"If true, print 10 spaces instead of 5, longer than a CUF sequence.");
|
||||
INIT_TEST_PROPERTY(bool, printTextAfterSpaces, L"If true, print \"ZZZZZ\" after the spaces on the first line.");
|
||||
|
||||
// See https://github.com/microsoft/terminal/issues/5039#issuecomment-606833841
|
||||
Log::Comment(L"This is a more than comprehensive test for GH#5039. We're "
|
||||
L"going to print some text to the buffer, then fill the alt-"
|
||||
L"buffer with text, then switch back to the main buffer. The "
|
||||
L"text from the alt buffer should not pollute the main buffer.");
|
||||
|
||||
// The text we're printing will look like one of the following, with the
|
||||
// cursor on the _
|
||||
// * cursorOnNextLine=false, useLongSpaces=false:
|
||||
// AAAAA ZZZZZ_
|
||||
// * cursorOnNextLine=false, useLongSpaces=true:
|
||||
// AAAAA ZZZZZ_
|
||||
// * cursorOnNextLine=true, useLongSpaces=false:
|
||||
// AAAAA ZZZZZ
|
||||
// BBBBB_
|
||||
// * cursorOnNextLine=true, useLongSpaces=true:
|
||||
// AAAAA ZZZZZ
|
||||
// BBBBB_
|
||||
//
|
||||
// If printTextAfterSpaces=false, then we won't print the "ZZZZZ"
|
||||
//
|
||||
// The interesting case that repros the bug in GH#5039 is
|
||||
// - paintEachNewline=DontPaintAfterNewlines (2)
|
||||
// - cursorOnNextLine=false
|
||||
// - paintAfterDECALN=<any>
|
||||
// - changeAttributes=true
|
||||
// - useLongSpaces=<any>
|
||||
// - printTextAfterSpaces=<any>
|
||||
//
|
||||
// All the possible cases are left here though, to catch potential future regressions.
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& hostSm = si.GetStateMachine();
|
||||
auto& hostTb = si.GetTextBuffer();
|
||||
auto& termTb = *term->_buffer;
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
auto verifyBuffer = [&cursorOnNextLine, &useLongSpaces, &printTextAfterSpaces](const TextBuffer& tb,
|
||||
const til::rectangle viewport) {
|
||||
// We _would_ expect the Terminal's cursor to be on { 8, 0 }, but this
|
||||
// is currently broken due to #381/#4676. So we'll use the viewport
|
||||
// provided to find the actual Y position of the cursor.
|
||||
const short viewTop = viewport.origin().y<short>();
|
||||
const short cursorRow = viewTop + (cursorOnNextLine ? 1 : 0);
|
||||
const short cursorCol = (cursorOnNextLine ? 5 :
|
||||
(10 + (useLongSpaces ? 5 : 0) + (printTextAfterSpaces ? 5 : 0)));
|
||||
const COORD expectedCursor{ cursorCol, cursorRow };
|
||||
|
||||
VERIFY_ARE_EQUAL(expectedCursor, tb.GetCursor().GetPosition());
|
||||
VERIFY_IS_TRUE(tb.GetCursor().IsVisible());
|
||||
auto iter = TestUtils::VerifyExpectedString(tb, L"AAAAA", { 0, viewTop });
|
||||
TestUtils::VerifyExpectedString(useLongSpaces ? L" " : L" ", iter);
|
||||
if (printTextAfterSpaces)
|
||||
{
|
||||
TestUtils::VerifyExpectedString(L"ZZZZZ", iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
TestUtils::VerifyExpectedString(L" ", iter);
|
||||
}
|
||||
TestUtils::VerifyExpectedString(L" ", iter);
|
||||
|
||||
if (cursorOnNextLine)
|
||||
{
|
||||
TestUtils::VerifyExpectedString(tb, L"BBBBB", { 0, cursorRow });
|
||||
}
|
||||
};
|
||||
|
||||
// We're _not_ checking the conpty output during this test, only the side effects.
|
||||
_checkConptyOutput = false;
|
||||
|
||||
gci.LockConsole(); // Lock must be taken to manipulate alt/main buffer state.
|
||||
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
|
||||
|
||||
Log::Comment(L"Setting up the host buffer...");
|
||||
hostSm.ProcessString(L"AAAAA");
|
||||
hostSm.ProcessString(useLongSpaces ? L" " : L" ");
|
||||
if (changeAttributes)
|
||||
{
|
||||
hostSm.ProcessString(L"\x1b[44m");
|
||||
}
|
||||
if (printTextAfterSpaces)
|
||||
{
|
||||
hostSm.ProcessString(L"ZZZZZ");
|
||||
}
|
||||
hostSm.ProcessString(L"\x1b[0m");
|
||||
|
||||
if (cursorOnNextLine)
|
||||
{
|
||||
hostSm.ProcessString(L"\n");
|
||||
hostSm.ProcessString(L"BBBBB");
|
||||
}
|
||||
Log::Comment(L"Painting after the initial setup.");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"Switching to the alt buffer and using DECALN to fill it with 'E's");
|
||||
hostSm.ProcessString(L"\x1b[?1049h");
|
||||
hostSm.ProcessString(L"\x1b#8");
|
||||
if (paintAfterDECALN)
|
||||
{
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
for (auto i = 0; i < si.GetViewport().Height(); i++)
|
||||
{
|
||||
hostSm.ProcessString(L"\n");
|
||||
if (paintEachNewline == PaintEveryNewline)
|
||||
{
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
}
|
||||
if (paintEachNewline == PaintAfterAllNewlines)
|
||||
{
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
Log::Comment(L"Returning to the main buffer.");
|
||||
hostSm.ProcessString(L"\x1b[?1049l");
|
||||
|
||||
Log::Comment(L"Checking the host buffer state");
|
||||
verifyBuffer(hostTb, si.GetViewport().ToInclusive());
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"Checking the terminal buffer state");
|
||||
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::OverstrikeAtBottomOfBuffer()
|
||||
{
|
||||
// See https://github.com/microsoft/terminal/pull/5181#issuecomment-607545241
|
||||
Log::Comment(L"This test replicates the zsh menu-complete functionality. In"
|
||||
L" the course of a single frame, we're going to both scroll "
|
||||
L"the frame and print multiple lines of text above the bottom line.");
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& hostSm = si.GetStateMachine();
|
||||
auto& hostTb = si.GetTextBuffer();
|
||||
auto& termTb = *term->_buffer;
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
auto verifyBuffer = [](const TextBuffer& tb,
|
||||
const til::rectangle viewport) {
|
||||
const auto lastRow = viewport.bottom<short>() - 1;
|
||||
const til::point expectedCursor{ 0, lastRow - 1 };
|
||||
VERIFY_ARE_EQUAL(expectedCursor, til::point{ tb.GetCursor().GetPosition() });
|
||||
VERIFY_IS_TRUE(tb.GetCursor().IsVisible());
|
||||
|
||||
TestUtils::VerifyExpectedString(tb, L"AAAAAAAAAA DDDDDDDDDD", til::point{ 0, lastRow - 2 });
|
||||
TestUtils::VerifyExpectedString(tb, L"BBBBBBBBBB", til::point{ 0, lastRow - 1 });
|
||||
TestUtils::VerifyExpectedString(tb, L"FFFFFFFFFE", til::point{ 0, lastRow });
|
||||
};
|
||||
|
||||
_logConpty = true;
|
||||
// We're _not_ checking the conpty output during this test, only the side effects.
|
||||
_checkConptyOutput = false;
|
||||
|
||||
hostSm.ProcessString(L"\x1b#8");
|
||||
|
||||
hostSm.ProcessString(L"\x1b[32;1H");
|
||||
|
||||
hostSm.ProcessString(L"\x1b[J");
|
||||
hostSm.ProcessString(L"AAAAAAAAAA");
|
||||
hostSm.ProcessString(L"\x1b[K");
|
||||
hostSm.ProcessString(L"\r");
|
||||
hostSm.ProcessString(L"\n");
|
||||
hostSm.ProcessString(L"BBBBBBBBBB");
|
||||
hostSm.ProcessString(L"\x1b[K");
|
||||
hostSm.ProcessString(L"\n");
|
||||
hostSm.ProcessString(L"CCCCCCCCCC");
|
||||
hostSm.ProcessString(L"\x1b[2A");
|
||||
hostSm.ProcessString(L"\r");
|
||||
hostSm.ProcessString(L"\x1b[20C");
|
||||
hostSm.ProcessString(L"DDDDDDDDDD");
|
||||
hostSm.ProcessString(L"\x1b[K");
|
||||
hostSm.ProcessString(L"\r");
|
||||
hostSm.ProcessString(L"\n");
|
||||
hostSm.ProcessString(L"\x1b[1B");
|
||||
hostSm.ProcessString(L"EEEEEEEEEE");
|
||||
hostSm.ProcessString(L"\r");
|
||||
hostSm.ProcessString(L"FFFFFFFFF");
|
||||
hostSm.ProcessString(L"\r");
|
||||
hostSm.ProcessString(L"\x1b[A");
|
||||
hostSm.ProcessString(L"\x1b[A");
|
||||
hostSm.ProcessString(L"\n");
|
||||
|
||||
Log::Comment(L"========== Checking the host buffer state ==========");
|
||||
verifyBuffer(hostTb, si.GetViewport().ToInclusive());
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"========== Checking the terminal buffer state ==========");
|
||||
|
||||
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::MarginsWithStatusLine()
|
||||
{
|
||||
// See https://github.com/microsoft/terminal/issues/5161
|
||||
//
|
||||
// This test reproduces a case from the MSYS/cygwin (runtime < 3.1) vim.
|
||||
// From what I can tell, they implement scrolling by emitting a newline at
|
||||
// the bottom of the buffer (to create a new blank line), then they use
|
||||
// ScrollConsoleScreenBuffer to shift the status line(s) down a line, and
|
||||
// then they re-printing the status line.
|
||||
Log::Comment(L"Newline, and scroll the bottom lines of the buffer down with"
|
||||
L" ScrollConsoleScreenBuffer to emulate how cygwin VIM works");
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& hostSm = si.GetStateMachine();
|
||||
auto& hostTb = si.GetTextBuffer();
|
||||
auto& termTb = *term->_buffer;
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
auto verifyBuffer = [](const TextBuffer& tb,
|
||||
const til::rectangle viewport) {
|
||||
const auto lastRow = viewport.bottom<short>() - 1;
|
||||
const til::point expectedCursor{ 1, lastRow };
|
||||
VERIFY_ARE_EQUAL(expectedCursor, til::point{ tb.GetCursor().GetPosition() });
|
||||
VERIFY_IS_TRUE(tb.GetCursor().IsVisible());
|
||||
|
||||
TestUtils::VerifyExpectedString(tb, L"EEEEEEEEEE", til::point{ 0, lastRow - 4 });
|
||||
TestUtils::VerifyExpectedString(tb, L"AAAAAAAAAA", til::point{ 0, lastRow - 3 });
|
||||
TestUtils::VerifyExpectedString(tb, L" ", til::point{ 0, lastRow - 2 });
|
||||
TestUtils::VerifyExpectedString(tb, L"XBBBBBBBBB", til::point{ 0, lastRow - 1 });
|
||||
TestUtils::VerifyExpectedString(tb, L"YCCCCCCCCC", til::point{ 0, lastRow });
|
||||
};
|
||||
|
||||
// We're _not_ checking the conpty output during this test, only the side effects.
|
||||
_checkConptyOutput = false;
|
||||
|
||||
// Use DECALN to fill the buffer with 'E's.
|
||||
hostSm.ProcessString(L"\x1b#8");
|
||||
|
||||
const short originalBottom = si.GetViewport().BottomInclusive();
|
||||
// Print 3 lines into the bottom of the buffer:
|
||||
// AAAAAAAAAA
|
||||
// BBBBBBBBBB
|
||||
// CCCCCCCCCC
|
||||
// In this test, the 'B' and 'C' lines represent the status lines at the
|
||||
// bottom of vim, and the 'A' line is a buffer line.
|
||||
hostSm.ProcessString(L"\x1b[30;1H");
|
||||
|
||||
hostSm.ProcessString(L"AAAAAAAAAA");
|
||||
hostSm.ProcessString(L"\n");
|
||||
hostSm.ProcessString(L"BBBBBBBBBB");
|
||||
hostSm.ProcessString(L"\n");
|
||||
hostSm.ProcessString(L"CCCCCCCCCC");
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
// After printing the 'C' line, the cursor is on the bottom line of the viewport.
|
||||
// Emit a newline here to get a new line at the bottom of the viewport.
|
||||
hostSm.ProcessString(L"\n");
|
||||
const short newBottom = si.GetViewport().BottomInclusive();
|
||||
|
||||
{
|
||||
// Emulate calling ScrollConsoleScreenBuffer to scroll the B and C lines
|
||||
// down one line.
|
||||
SMALL_RECT src;
|
||||
src.Top = newBottom - 2;
|
||||
src.Left = 0;
|
||||
src.Right = si.GetViewport().Width();
|
||||
src.Bottom = originalBottom;
|
||||
COORD tgt = { 0, newBottom - 1 };
|
||||
TextAttribute useThisAttr(0x07); // We don't terribly care about the attributes so this is arbitrary
|
||||
ScrollRegion(si, src, std::nullopt, tgt, L' ', useThisAttr);
|
||||
}
|
||||
|
||||
// Move the cursor to the location of the B line
|
||||
hostSm.ProcessString(L"\x1b[31;1H");
|
||||
|
||||
// Print an 'X' on the 'B' line, and a 'Y' on the 'C' line.
|
||||
hostSm.ProcessString(L"X");
|
||||
hostSm.ProcessString(L"\n");
|
||||
hostSm.ProcessString(L"Y");
|
||||
|
||||
Log::Comment(L"========== Checking the host buffer state ==========");
|
||||
verifyBuffer(hostTb, si.GetViewport().ToInclusive());
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"========== Checking the terminal buffer state ==========");
|
||||
|
||||
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::OutputWrappedLineWithSpace()
|
||||
{
|
||||
// See https://github.com/microsoft/terminal/pull/5181#issuecomment-610110348
|
||||
Log::Comment(L"Ensures that a buffer line in conhost that wrapped _on a "
|
||||
L"space_ will still be emitted as wrapped.");
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
auto& hostTb = si.GetTextBuffer();
|
||||
auto& termTb = *term->_buffer;
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
const auto firstTextLength = TerminalViewWidth - 2;
|
||||
const auto spacesLength = 3;
|
||||
const auto secondTextLength = 1;
|
||||
|
||||
sm.ProcessString(std::wstring(firstTextLength, L'A'));
|
||||
sm.ProcessString(std::wstring(spacesLength, L' '));
|
||||
sm.ProcessString(std::wstring(secondTextLength, L'B'));
|
||||
|
||||
auto verifyBuffer = [&](const TextBuffer& tb) {
|
||||
// Buffer contents should look like the following: (80 wide)
|
||||
// (w) means we hard wrapped the line
|
||||
// (b) means the line is _not_ wrapped (it's broken, the default state.)
|
||||
//
|
||||
// |AAAA...AA | (w)
|
||||
// | B_ ... | (b) (cursor is on the '_')
|
||||
// | ... | (b)
|
||||
|
||||
VERIFY_IS_TRUE(tb.GetRowByOffset(0).GetCharRow().WasWrapForced());
|
||||
VERIFY_IS_FALSE(tb.GetRowByOffset(1).GetCharRow().WasWrapForced());
|
||||
|
||||
// First row
|
||||
auto iter0 = tb.GetCellDataAt({ 0, 0 });
|
||||
TestUtils::VerifySpanOfText(L"A", iter0, 0, firstTextLength);
|
||||
TestUtils::VerifySpanOfText(L" ", iter0, 0, 2);
|
||||
|
||||
// Second row
|
||||
auto iter1 = tb.GetCellDataAt({ 0, 1 });
|
||||
TestUtils::VerifySpanOfText(L" ", iter1, 0, 1);
|
||||
auto iter2 = tb.GetCellDataAt({ 1, 1 });
|
||||
TestUtils::VerifySpanOfText(L"B", iter2, 0, secondTextLength);
|
||||
};
|
||||
|
||||
Log::Comment(L"========== Checking the host buffer state ==========");
|
||||
verifyBuffer(hostTb);
|
||||
|
||||
std::string firstLine = std::string(firstTextLength, 'A');
|
||||
firstLine += " ";
|
||||
std::string secondLine{ " B" };
|
||||
|
||||
expectedOutput.push_back(firstLine);
|
||||
expectedOutput.push_back(secondLine);
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"========== Checking the terminal buffer state ==========");
|
||||
verifyBuffer(termTb);
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::OutputWrappedLineWithSpaceAtBottomOfBuffer()
|
||||
{
|
||||
// See https://github.com/microsoft/terminal/pull/5181#issuecomment-610110348
|
||||
// This is the same test as OutputWrappedLineWithSpace, but at the bottom of
|
||||
// the buffer, so we get scrolling behavior as well.
|
||||
Log::Comment(L"Ensures that a buffer line in conhost that wrapped _on a "
|
||||
L"space_ will still be emitted as wrapped.");
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
auto& hostTb = si.GetTextBuffer();
|
||||
auto& termTb = *term->_buffer;
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
// First, fill the buffer with contents, so conpty starts circling
|
||||
const auto hostView = si.GetViewport();
|
||||
const auto end = 2 * hostView.Height();
|
||||
for (auto i = 0; i < end; i++)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"Writing line %d/%d", i, end));
|
||||
expectedOutput.push_back("X");
|
||||
if (i < hostView.BottomInclusive())
|
||||
{
|
||||
expectedOutput.push_back("\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// After we hit the bottom of the viewport, the newlines come in
|
||||
// separated by empty writes for whatever reason.
|
||||
expectedOutput.push_back("\r");
|
||||
expectedOutput.push_back("\n");
|
||||
expectedOutput.push_back("");
|
||||
}
|
||||
|
||||
sm.ProcessString(L"X\n");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
const auto firstTextLength = TerminalViewWidth - 2;
|
||||
const auto spacesLength = 3;
|
||||
const auto secondTextLength = 1;
|
||||
|
||||
std::string firstLine = std::string(firstTextLength, 'A');
|
||||
firstLine += " ";
|
||||
std::string secondLine{ " B" };
|
||||
|
||||
// The following diagrams show the buffer contents after each string emitted
|
||||
// from conpty. For each of these diagrams:
|
||||
// (w) means we hard wrapped the line
|
||||
// (b) means the line is _not_ wrapped (it's broken, the default state.)
|
||||
// cursor is on the '_'
|
||||
|
||||
// Initial state:
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |_ | (b)
|
||||
|
||||
expectedOutput.push_back(firstLine);
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AA _| (w) The cursor is actually on the last ' ' here
|
||||
|
||||
// TODO GH#5228 might break the "newline & repaint the wrapped char" checks here, that's okay.
|
||||
expectedOutput.push_back("\r"); // This \r\n is emitted by ScrollFrame to
|
||||
expectedOutput.push_back("\n"); // add a newline to the bottom of the buffer
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AA | (b)
|
||||
// |_ | (b)
|
||||
|
||||
expectedOutput.push_back("\x1b[31;80H"); // Move the cursor BACK to the wrapped row
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AA _| (b) The cursor is actually on the last ' ' here
|
||||
// | | (b)
|
||||
|
||||
expectedOutput.push_back(std::string(1, ' ')); // Reprint the last character of the wrapped row
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AA |_ (w) The cursor is actually on the last ' ' here
|
||||
// | | (b)
|
||||
|
||||
expectedOutput.push_back(secondLine);
|
||||
// |X | (b)
|
||||
// |X | (b)
|
||||
// ...
|
||||
// |X | (b)
|
||||
// |AAAAAAAA...AA | (w)
|
||||
// | B_ | (b)
|
||||
|
||||
sm.ProcessString(std::wstring(firstTextLength, L'A'));
|
||||
sm.ProcessString(std::wstring(spacesLength, L' '));
|
||||
sm.ProcessString(std::wstring(secondTextLength, L'B'));
|
||||
|
||||
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport) {
|
||||
// Buffer contents should look like the following: (80 wide)
|
||||
// (w) means we hard wrapped the line
|
||||
// (b) means the line is _not_ wrapped (it's broken, the default state.)
|
||||
//
|
||||
// |AAAA...AA | (w)
|
||||
// | B_ ... | (b) (cursor is on the '_')
|
||||
// | ... | (b)
|
||||
|
||||
const short wrappedRow = viewport.bottom<short>() - 2;
|
||||
VERIFY_IS_TRUE(tb.GetRowByOffset(wrappedRow).GetCharRow().WasWrapForced());
|
||||
VERIFY_IS_FALSE(tb.GetRowByOffset(wrappedRow + 1).GetCharRow().WasWrapForced());
|
||||
|
||||
// First row
|
||||
auto iter0 = tb.GetCellDataAt({ 0, wrappedRow });
|
||||
TestUtils::VerifySpanOfText(L"A", iter0, 0, firstTextLength);
|
||||
TestUtils::VerifySpanOfText(L" ", iter0, 0, 2);
|
||||
|
||||
// Second row
|
||||
auto iter1 = tb.GetCellDataAt({ 0, wrappedRow + 1 });
|
||||
TestUtils::VerifySpanOfText(L" ", iter1, 0, 1);
|
||||
auto iter2 = tb.GetCellDataAt({ 1, wrappedRow + 1 });
|
||||
TestUtils::VerifySpanOfText(L"B", iter2, 0, secondTextLength);
|
||||
};
|
||||
|
||||
Log::Comment(L"========== Checking the host buffer state ==========");
|
||||
verifyBuffer(hostTb, hostView.ToInclusive());
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"========== Checking the terminal buffer state ==========");
|
||||
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
|
||||
}
|
||||
|
||||
@@ -48,12 +48,12 @@ namespace TerminalCoreUnitTests
|
||||
// Verify that Alt+a generates a lowercase a on the input
|
||||
expectedinput = L"\x1b"
|
||||
"a";
|
||||
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', 0, ControlKeyStates::LeftAltPressed));
|
||||
VERIFY_IS_TRUE(term.SendCharEvent(L'a', 0, ControlKeyStates::LeftAltPressed));
|
||||
|
||||
// Verify that Alt+shift+a generates a uppercase a on the input
|
||||
expectedinput = L"\x1b"
|
||||
"A";
|
||||
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', 0, ControlKeyStates::LeftAltPressed | ControlKeyStates::ShiftPressed));
|
||||
VERIFY_IS_TRUE(term.SendCharEvent(L'A', 0, ControlKeyStates::LeftAltPressed | ControlKeyStates::ShiftPressed));
|
||||
}
|
||||
|
||||
void InputTest::AltSpace()
|
||||
@@ -62,5 +62,6 @@ namespace TerminalCoreUnitTests
|
||||
// bring up the system menu for restore, min/maximize, size, move,
|
||||
// close
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(L' ', 0, ControlKeyStates::LeftAltPressed));
|
||||
VERIFY_IS_FALSE(term.SendCharEvent(L' ', 0, ControlKeyStates::LeftAltPressed));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ AppHost::AppHost() noexcept :
|
||||
_logic,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
|
||||
_window->MouseScrolled({ this, &AppHost::_WindowMouseWheeled });
|
||||
_window->MakeWindow();
|
||||
}
|
||||
|
||||
@@ -338,3 +338,48 @@ void AppHost::_ToggleFullscreen(const winrt::Windows::Foundation::IInspectable&,
|
||||
{
|
||||
_window->ToggleFullscreen();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the IslandWindow has received a WM_MOUSEWHEEL message. This can
|
||||
// happen on some laptops, where their trackpads won't scroll inactive windows
|
||||
// _ever_.
|
||||
// - We're going to take that message and manually plumb it through to our
|
||||
// TermControl's, or anything else that implements IMouseWheelListener.
|
||||
// - See GH#979 for more details.
|
||||
// Arguments:
|
||||
// - coord: The Window-relative, logical coordinates location of the mouse during this event.
|
||||
// - delta: the wheel delta that triggered this event.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta)
|
||||
{
|
||||
if (_logic)
|
||||
{
|
||||
// Find all the elements that are underneath the mouse
|
||||
auto elems = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::FindElementsInHostCoordinates(coord, _logic.GetRoot());
|
||||
for (const auto& e : elems)
|
||||
{
|
||||
// If that element has implemented IMouseWheelListener, call OnMouseWheel on that element.
|
||||
if (auto control{ e.try_as<winrt::Microsoft::Terminal::TerminalControl::IMouseWheelListener>() })
|
||||
{
|
||||
try
|
||||
{
|
||||
// Translate the event to the coordinate space of the control
|
||||
// we're attempting to dispatch it to
|
||||
const auto transform = e.TransformToVisual(nullptr);
|
||||
const til::point controlOrigin{ til::math::flooring, transform.TransformPoint(til::point{ 0, 0 }) };
|
||||
|
||||
const til::point offsetPoint = coord - controlOrigin;
|
||||
|
||||
if (control.OnMouseWheel(offsetPoint, delta))
|
||||
{
|
||||
// If the element handled the mouse wheel event, don't
|
||||
// continue to iterate over the remaining controls.
|
||||
break;
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,5 @@ private:
|
||||
const winrt::Windows::UI::Xaml::ElementTheme& arg);
|
||||
void _ToggleFullscreen(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::TerminalApp::ToggleFullscreenEventArgs& arg);
|
||||
void _WindowMouseWheeled(const til::point coord, const int32_t delta);
|
||||
};
|
||||
|
||||
@@ -326,6 +326,40 @@ void IslandWindow::OnSize(const UINT width, const UINT height)
|
||||
_windowCloseButtonClickedHandler();
|
||||
return 0;
|
||||
}
|
||||
case WM_MOUSEWHEEL:
|
||||
try
|
||||
{
|
||||
// This whole handler is a hack for GH#979.
|
||||
//
|
||||
// On some laptops, their trackpads won't scroll inactive windows
|
||||
// _ever_. With our entire window just being one giant XAML Island, the
|
||||
// touchpad driver thinks our entire window is inactive, and won't
|
||||
// scroll the XAML island. On those types of laptops, we'll get a
|
||||
// WM_MOUSEWHEEL here, in our root window, when the trackpad scrolls.
|
||||
// We're going to take that message and manually plumb it through to our
|
||||
// TermControl's, or anything else that implements IMouseWheelListener.
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
|
||||
// Important! Do not use the LOWORD or HIWORD macros to extract the x-
|
||||
// and y- coordinates of the cursor position because these macros return
|
||||
// incorrect results on systems with multiple monitors. Systems with
|
||||
// multiple monitors can have negative x- and y- coordinates, and LOWORD
|
||||
// and HIWORD treat the coordinates as unsigned quantities.
|
||||
const til::point eventPoint{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
|
||||
// This mouse event is relative to the display origin, not the window. Convert here.
|
||||
const til::rectangle windowRect{ GetWindowRect() };
|
||||
const auto origin = windowRect.origin();
|
||||
const auto relative = eventPoint - origin;
|
||||
// Convert to logical scaling before raising the event.
|
||||
const auto real = relative / GetCurrentDpiScale();
|
||||
|
||||
const short wheelDelta = static_cast<short>(HIWORD(wparam));
|
||||
|
||||
// Raise an event, so any listeners can handle the mouse wheel event manually.
|
||||
_MouseScrolledHandlers(real, wheelDelta);
|
||||
return 0;
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// TODO: handle messages here...
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
|
||||
DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
|
||||
WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
|
||||
|
||||
protected:
|
||||
void ForceResize()
|
||||
|
||||
@@ -366,7 +366,9 @@ int NonClientIslandWindow::_GetResizeHandleHeight() const noexcept
|
||||
// GH#1438 - Attempt to detect if there's an autohide taskbar, and if there
|
||||
// is, reduce our size a bit on the side with the taskbar, so the user can
|
||||
// still mouse-over the taskbar to reveal it.
|
||||
HMONITOR hMon = MonitorFromWindow(_window.get(), MONITOR_DEFAULTTONULL);
|
||||
// GH#5209 - make sure to use MONITOR_DEFAULTTONEAREST, so that this will
|
||||
// still find the right monitor even when we're restoring from minimized.
|
||||
HMONITOR hMon = MonitorFromWindow(_window.get(), MONITOR_DEFAULTTONEAREST);
|
||||
if (hMon && (_isMaximized || _fullscreen))
|
||||
{
|
||||
MONITORINFO monInfo{ 0 };
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user