mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-20 13:57:43 +00:00
Compare commits
71 Commits
dev/mgirie
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
516a923ac6 | ||
|
|
ad9900857a | ||
|
|
0cc9bd0aad | ||
|
|
b371eed864 | ||
|
|
45af92d1af | ||
|
|
5b51af1021 | ||
|
|
4f6f3b98b8 | ||
|
|
6268a4779c | ||
|
|
13bc71de3c | ||
|
|
a0670cb6b3 | ||
|
|
7908164f9d | ||
|
|
8ffea2c177 | ||
|
|
e0853ae4cc | ||
|
|
c089ae0c57 | ||
|
|
8d81497eb7 | ||
|
|
717ea85c9f | ||
|
|
efea1e5bad | ||
|
|
871b8de74f | ||
|
|
e4c5e8bd2a | ||
|
|
1acfef60f6 | ||
|
|
de379cd043 | ||
|
|
7112f4e081 | ||
|
|
7423734a48 | ||
|
|
07dc0601f9 | ||
|
|
6f42367ab8 | ||
|
|
d2c72e5c25 | ||
|
|
92437d718f | ||
|
|
817f598e20 | ||
|
|
2c5a35f1be | ||
|
|
ea58e4036b | ||
|
|
ee8800c739 | ||
|
|
f7b0f7444a | ||
|
|
1b6e6bd6dd | ||
|
|
7b6df26411 | ||
|
|
f3cc4c0328 | ||
|
|
f3a49fafe3 | ||
|
|
15c02b77a0 | ||
|
|
2c3368f766 | ||
|
|
b1131263cf | ||
|
|
c53fe1c2bf | ||
|
|
f9a844dbda | ||
|
|
23a19c5818 | ||
|
|
7712104983 | ||
|
|
608a49e817 | ||
|
|
10992b77a0 | ||
|
|
f6f5598c9c | ||
|
|
f681d3a1c1 | ||
|
|
d07546a6fe | ||
|
|
ed7c716978 | ||
|
|
0c901edd81 | ||
|
|
acf1ddc9c4 | ||
|
|
cb2f347c2f | ||
|
|
49874d1b9e | ||
|
|
cfdf03c24b | ||
|
|
70d44c84c8 | ||
|
|
1678b58dde | ||
|
|
482dcec60a | ||
|
|
46fd7caf5a | ||
|
|
638c6d0291 | ||
|
|
68294f863d | ||
|
|
59f184aa2d | ||
|
|
a544f56e17 | ||
|
|
29be8564f6 | ||
|
|
5d36e5d2df | ||
|
|
0220f71883 | ||
|
|
70560a789c | ||
|
|
a0edb12cd6 | ||
|
|
d3f9859051 | ||
|
|
f1dc649135 | ||
|
|
9eb9bc9235 | ||
|
|
d465a47bc5 |
15
.github/actions/spelling/README.md
vendored
Normal file
15
.github/actions/spelling/README.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# check-spelling/check-spelling configuration
|
||||
|
||||
File | Purpose | Format | Info
|
||||
-|-|-|-
|
||||
[allow/*.txt](allow/) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow)
|
||||
[reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject)
|
||||
[excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
|
||||
[patterns/*.txt](patterns/) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns)
|
||||
[line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[expect/*.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect)
|
||||
[advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice)
|
||||
|
||||
Note: you can replace any of these files with a directory by the same name (minus the suffix)
|
||||
and then include multiple files inside that directory (with that suffix) to merge multiple files together.
|
||||
38
.github/actions/spelling/advice.md
vendored
38
.github/actions/spelling/advice.md
vendored
@@ -1,4 +1,4 @@
|
||||
<!-- markdownlint-disable MD033 MD041 -->
|
||||
<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
|
||||
<details>
|
||||
<summary>
|
||||
:pencil2: Contributor please read this
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
|
||||
|
||||
:warning: The command is written for posix shells. You can copy the contents of each `perl` command excluding the outer `'` marks and dropping any `'"`/`"'` quotation mark pairs into a file and then run `perl file.pl` from the root of the repository to run the code. Alternatively, you can manually insert the items...
|
||||
:warning: The command is written for posix shells. If it doesn't work for you, you can manually _add_ (one word per line) / _remove_ items to `expect.txt` and the `excludes.txt` files.
|
||||
|
||||
If the listed items are:
|
||||
|
||||
@@ -20,31 +20,29 @@ See the `README.md` in each directory for more information.
|
||||
|
||||
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
|
||||
|
||||
<details><summary>:clamp: If you see a bunch of garbage</summary>
|
||||
|
||||
If it relates to a ...
|
||||
<details><summary>well-formed pattern</summary>
|
||||
<details><summary>If the flagged items are :exploding_head: false positives</summary>
|
||||
|
||||
See if there's a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it.
|
||||
If items relate to a ...
|
||||
* binary file (or some other file you wouldn't want to check at all).
|
||||
|
||||
If not, try writing one and adding it to a `patterns/{file}.txt`.
|
||||
Please add a file path to the `excludes.txt` file matching the containing file.
|
||||
|
||||
Patterns are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
|
||||
|
||||
Note that patterns can't match multiline strings.
|
||||
</details>
|
||||
<details><summary>binary-ish string</summary>
|
||||
|
||||
Please add a file path to the `excludes.txt` file instead of just accepting the garbage.
|
||||
|
||||
File paths are Perl 5 Regular Expressions - you can [test](
|
||||
File paths are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
|
||||
|
||||
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
|
||||
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
|
||||
../tree/HEAD/README.md) (on whichever branch you're using).
|
||||
</details>
|
||||
|
||||
|
||||
* well-formed pattern.
|
||||
|
||||
If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
|
||||
try adding it to the `patterns.txt` file.
|
||||
|
||||
Patterns are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
|
||||
|
||||
Note that patterns can't match multiline strings.
|
||||
</details>
|
||||
|
||||
</details>
|
||||
|
||||
43
.github/actions/spelling/allow/allow.txt
vendored
43
.github/actions/spelling/allow/allow.txt
vendored
@@ -1,45 +1,75 @@
|
||||
admins
|
||||
allcolors
|
||||
Apc
|
||||
apc
|
||||
breadcrumb
|
||||
breadcrumbs
|
||||
bsd
|
||||
calt
|
||||
ccmp
|
||||
Apc
|
||||
changelog
|
||||
clickable
|
||||
clig
|
||||
CMMI
|
||||
copyable
|
||||
cybersecurity
|
||||
dalet
|
||||
dcs
|
||||
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
|
||||
@@ -54,16 +84,25 @@ 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
|
||||
|
||||
66
.github/actions/spelling/allow/apis.txt
vendored
66
.github/actions/spelling/allow/apis.txt
vendored
@@ -1,28 +1,44 @@
|
||||
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
|
||||
@@ -35,12 +51,16 @@ fullkbd
|
||||
futex
|
||||
GETDESKWALLPAPER
|
||||
GETHIGHCONTRAST
|
||||
GETMOUSEHOVERTIME
|
||||
Hashtable
|
||||
HIGHCONTRASTON
|
||||
HIGHCONTRASTW
|
||||
hotkeys
|
||||
href
|
||||
hrgn
|
||||
HTCLOSE
|
||||
hwinsta
|
||||
HWINSTA
|
||||
IActivation
|
||||
IApp
|
||||
IAppearance
|
||||
@@ -57,18 +77,22 @@ IDirect
|
||||
IExplorer
|
||||
IFACEMETHOD
|
||||
IFile
|
||||
IGraphics
|
||||
IInheritable
|
||||
IMap
|
||||
IMonarch
|
||||
IObject
|
||||
iosfwd
|
||||
IPackage
|
||||
IPeasant
|
||||
ISetup
|
||||
isspace
|
||||
IStorage
|
||||
istream
|
||||
IStringable
|
||||
ITab
|
||||
ITaskbar
|
||||
itow
|
||||
IUri
|
||||
IVirtual
|
||||
KEYSELECT
|
||||
@@ -77,13 +101,27 @@ llabs
|
||||
llu
|
||||
localtime
|
||||
lround
|
||||
Lsa
|
||||
lsass
|
||||
LSHIFT
|
||||
LTGRAY
|
||||
MAINWINDOW
|
||||
memchr
|
||||
memicmp
|
||||
MENUCOMMAND
|
||||
MENUDATA
|
||||
MENUINFO
|
||||
MENUITEMINFOW
|
||||
mmeapi
|
||||
MOUSELEAVE
|
||||
mov
|
||||
mptt
|
||||
msappx
|
||||
MULTIPLEUSE
|
||||
NCHITTEST
|
||||
NCLBUTTONDBLCLK
|
||||
NCMOUSELEAVE
|
||||
NCMOUSEMOVE
|
||||
NCRBUTTONDBLCLK
|
||||
NIF
|
||||
NIN
|
||||
@@ -93,6 +131,7 @@ NOCHANGEDIR
|
||||
NOPROGRESS
|
||||
NOREDIRECTIONBITMAP
|
||||
NOREPEAT
|
||||
NOTIFYBYPOS
|
||||
NOTIFYICON
|
||||
NOTIFYICONDATA
|
||||
ntprivapi
|
||||
@@ -100,26 +139,36 @@ oaidl
|
||||
ocidl
|
||||
ODR
|
||||
offsetof
|
||||
ofstream
|
||||
onefuzz
|
||||
osver
|
||||
OSVERSIONINFOEXW
|
||||
otms
|
||||
OUTLINETEXTMETRICW
|
||||
overridable
|
||||
PACL
|
||||
PAGESCROLL
|
||||
PATINVERT
|
||||
PEXPLICIT
|
||||
PICKFOLDERS
|
||||
pmr
|
||||
ptstr
|
||||
QUERYENDSESSION
|
||||
rcx
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
ROOTOWNER
|
||||
roundf
|
||||
RSHIFT
|
||||
SACL
|
||||
schandle
|
||||
semver
|
||||
serializer
|
||||
SETVERSION
|
||||
SHELLEXECUTEINFOW
|
||||
shobjidl
|
||||
SHOWHIDE
|
||||
SHOWMINIMIZED
|
||||
SHOWTIP
|
||||
SINGLEUSE
|
||||
@@ -140,22 +189,37 @@ 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
|
||||
@@ -172,6 +236,7 @@ xlocmes
|
||||
xlocmon
|
||||
xlocnum
|
||||
xloctime
|
||||
XMax
|
||||
xmemory
|
||||
XParse
|
||||
xpath
|
||||
@@ -180,3 +245,4 @@ xstring
|
||||
xtree
|
||||
xutility
|
||||
YIcon
|
||||
YMax
|
||||
|
||||
8
.github/actions/spelling/allow/math.txt
vendored
8
.github/actions/spelling/allow/math.txt
vendored
@@ -1,3 +1,11 @@
|
||||
atan
|
||||
CPrime
|
||||
HBar
|
||||
HPrime
|
||||
isnan
|
||||
LPrime
|
||||
LStep
|
||||
powf
|
||||
RSub
|
||||
sqrtf
|
||||
ULP
|
||||
|
||||
13
.github/actions/spelling/allow/microsoft.txt
vendored
13
.github/actions/spelling/allow/microsoft.txt
vendored
@@ -1,5 +1,6 @@
|
||||
ACLs
|
||||
ADMINS
|
||||
advapi
|
||||
altform
|
||||
altforms
|
||||
appendwttlogging
|
||||
@@ -15,8 +16,10 @@ CPLs
|
||||
cpptools
|
||||
cppvsdbg
|
||||
CPRs
|
||||
cryptbase
|
||||
DACL
|
||||
DACLs
|
||||
defaultlib
|
||||
diffs
|
||||
disposables
|
||||
dotnetfeed
|
||||
@@ -24,15 +27,22 @@ DTDs
|
||||
DWINRT
|
||||
enablewttlogging
|
||||
Intelli
|
||||
IVisual
|
||||
libucrt
|
||||
libucrtd
|
||||
LKG
|
||||
LOCKFILE
|
||||
Lxss
|
||||
mfcribbon
|
||||
microsoft
|
||||
microsoftonline
|
||||
MSAA
|
||||
msixbundle
|
||||
MSVC
|
||||
MSVCP
|
||||
muxc
|
||||
netcore
|
||||
Onefuzz
|
||||
osgvsowi
|
||||
PFILETIME
|
||||
pgc
|
||||
@@ -43,6 +53,7 @@ powershell
|
||||
propkey
|
||||
pscustomobject
|
||||
QWORD
|
||||
regedit
|
||||
robocopy
|
||||
SACLs
|
||||
sdkddkver
|
||||
@@ -56,6 +67,8 @@ systemroot
|
||||
taskkill
|
||||
tasklist
|
||||
tdbuildteamid
|
||||
ucrt
|
||||
ucrtd
|
||||
unvirtualized
|
||||
VCRT
|
||||
vcruntime
|
||||
|
||||
14
.github/actions/spelling/allow/names.txt
vendored
14
.github/actions/spelling/allow/names.txt
vendored
@@ -1,14 +1,18 @@
|
||||
Anup
|
||||
austdi
|
||||
arkthur
|
||||
Ballmer
|
||||
bhoj
|
||||
Bhojwani
|
||||
Bluloco
|
||||
carlos
|
||||
dhowett
|
||||
Diviness
|
||||
dsafa
|
||||
duhowett
|
||||
DXP
|
||||
ekg
|
||||
eryksun
|
||||
ethanschoonover
|
||||
Firefox
|
||||
Gatta
|
||||
@@ -20,6 +24,7 @@ Hernan
|
||||
Howett
|
||||
Illhardt
|
||||
iquilezles
|
||||
italo
|
||||
jantari
|
||||
jerrysh
|
||||
Kaiyu
|
||||
@@ -31,8 +36,11 @@ Kourosh
|
||||
kowalczyk
|
||||
leonmsft
|
||||
Lepilleur
|
||||
lhecker
|
||||
lukesampson
|
||||
Macbook
|
||||
Manandhar
|
||||
masserano
|
||||
mbadolato
|
||||
Mehrain
|
||||
menger
|
||||
@@ -52,6 +60,7 @@ oldnewthing
|
||||
opengl
|
||||
osgwiki
|
||||
pabhojwa
|
||||
panos
|
||||
paulcam
|
||||
pauldotknopf
|
||||
PGP
|
||||
@@ -60,12 +69,17 @@ Rincewind
|
||||
rprichard
|
||||
Schoonover
|
||||
shadertoy
|
||||
Shomnipotence
|
||||
simioni
|
||||
Somuah
|
||||
sonph
|
||||
sonpham
|
||||
stakx
|
||||
talo
|
||||
thereses
|
||||
Walisch
|
||||
WDX
|
||||
Wellons
|
||||
Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
|
||||
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
@@ -0,0 +1,523 @@
|
||||
# marker to ignore all code on line
|
||||
^.*/\* #no-spell-check-line \*/.*$
|
||||
# marker for ignoring a comment to the end of the line
|
||||
// #no-spell-check.*$
|
||||
|
||||
# patch hunk comments
|
||||
^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .*
|
||||
# git index header
|
||||
index [0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
|
||||
|
||||
# cid urls
|
||||
(['"])cid:.*?\g{-1}
|
||||
|
||||
# data url in parens
|
||||
\(data:[^)]*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\)
|
||||
# data url in quotes
|
||||
([`'"])data:.*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
|
||||
# data url
|
||||
data:[-a-zA-Z=;:/0-9+]*,\S*
|
||||
|
||||
# mailto urls
|
||||
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
|
||||
|
||||
# magnet urls
|
||||
magnet:[?=:\w]+
|
||||
|
||||
# magnet urls
|
||||
"magnet:[^"]+"
|
||||
|
||||
# obs:
|
||||
"obs:[^"]*"
|
||||
|
||||
# The `\b` here means a break, it's the fancy way to handle urls, but it makes things harder to read
|
||||
# In this examples content, I'm using a number of different ways to match things to show various approaches
|
||||
# asciinema
|
||||
\basciinema\.org/a/[0-9a-zA-Z]+
|
||||
|
||||
# apple
|
||||
\bdeveloper\.apple\.com/[-\w?=/]+
|
||||
# Apple music
|
||||
\bembed\.music\.apple\.com/fr/playlist/usr-share/[-\w.]+
|
||||
|
||||
# appveyor api
|
||||
\bci\.appveyor\.com/api/projects/status/[0-9a-z]+
|
||||
# appveyor project
|
||||
\bci\.appveyor\.com/project/(?:[^/\s"]*/){2}builds?/\d+/job/[0-9a-z]+
|
||||
|
||||
# Amazon
|
||||
|
||||
# Amazon
|
||||
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
|
||||
# AWS S3
|
||||
\b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS execute-api
|
||||
\b[0-9a-z]{10}\.execute-api\.[-0-9a-z]+\.amazonaws\.com\b
|
||||
# AWS ELB
|
||||
\b\w+\.[-0-9a-z]+\.elb\.amazonaws\.com\b
|
||||
# AWS SNS
|
||||
\bsns\.[-0-9a-z]+.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS VPC
|
||||
vpc-\w+
|
||||
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
|
||||
# YouTube music
|
||||
\bmusic\.youtube\.com/youtubei/v1/browse(?:[?&]\w+=[-a-zA-Z0-9?&=_]*)
|
||||
# YouTube tag
|
||||
<\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"]
|
||||
# YouTube image
|
||||
\bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]*
|
||||
# Google Accounts
|
||||
\baccounts.google.com/[-_/?=.:;+%&0-9a-zA-Z]*
|
||||
# Google Analytics
|
||||
\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
|
||||
# Google APIs
|
||||
\bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+
|
||||
# Google Storage
|
||||
\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
|
||||
# Google Calendar
|
||||
\bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+
|
||||
\w+\@group\.calendar\.google\.com\b
|
||||
# Google DataStudio
|
||||
\bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|)
|
||||
# The leading `/` here is as opposed to the `\b` above
|
||||
# ... a short way to match `https://` or `http://` since most urls have one of those prefixes
|
||||
# Google Docs
|
||||
/docs\.google\.com/[a-z]+/(?:ccc\?key=\w+|(?:u/\d+|d/(?:e/|)[0-9a-zA-Z_-]+/)?(?:edit\?[-\w=#.]*|/\?[\w=&]*|))
|
||||
# Google Drive
|
||||
\bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]*
|
||||
# Google Groups
|
||||
\bgroups\.google\.com/(?:(?:forum/#!|d/)(?:msg|topics?|searchin)|a)/[^/\s"]+/[-a-zA-Z0-9$]+(?:/[-a-zA-Z0-9]+)*
|
||||
# Google Maps
|
||||
\bmaps\.google\.com/maps\?[\w&;=]*
|
||||
# Google themes
|
||||
themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
|
||||
# Google CDN
|
||||
\bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]*
|
||||
# Goo.gl
|
||||
/goo\.gl/[a-zA-Z0-9]+
|
||||
# Google Chrome Store
|
||||
\bchrome\.google\.com/webstore/detail/[-\w]*(?:/\w*|)
|
||||
# Google Books
|
||||
\bgoogle\.(?:\w{2,4})/books(?:/\w+)*\?[-\w\d=&#.]*
|
||||
# Google Fonts
|
||||
\bfonts\.(?:googleapis|gstatic)\.com/[-/?=:;+&0-9a-zA-Z]*
|
||||
# Google Forms
|
||||
\bforms\.gle/\w+
|
||||
# Google Scholar
|
||||
\bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+
|
||||
# Google Colab Research Drive
|
||||
\bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]*
|
||||
|
||||
# GitHub SHAs (api)
|
||||
\bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b
|
||||
# GitHub SHAs (markdown)
|
||||
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
|
||||
# GitHub SHAs
|
||||
\bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b
|
||||
# GitHub wiki
|
||||
\bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b
|
||||
# githubusercontent
|
||||
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
# githubassets
|
||||
\bgithubassets.com/[0-9a-f]+(?:[-/\w.]+)
|
||||
# gist github
|
||||
\bgist\.github\.com/[^/\s"]+/[0-9a-f]+
|
||||
# git.io
|
||||
\bgit\.io/[0-9a-zA-Z]+
|
||||
# GitHub JSON
|
||||
"node_id": "[-a-zA-Z=;:/0-9+]*"
|
||||
# Contributor
|
||||
\[[^\]]+\]\(https://github\.com/[^/\s"]+\)
|
||||
# GHSA
|
||||
GHSA(?:-[0-9a-z]{4}){3}
|
||||
|
||||
# GitLab commit
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
|
||||
# GitLab merge requests
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/-/merge_requests/\d+/diffs#[0-9a-f]{40}\b
|
||||
# GitLab uploads
|
||||
\bgitlab\.[^/\s"]*/uploads/[-a-zA-Z=;:/0-9+]*
|
||||
# GitLab commits
|
||||
\bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b
|
||||
|
||||
# binanace
|
||||
accounts.binance.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
|
||||
|
||||
# bitbucket diff
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+
|
||||
# bitbucket repositories commits
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
# bitbucket commits
|
||||
\bbitbucket\.org/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
|
||||
# bit.ly
|
||||
\bbit\.ly/\w+
|
||||
|
||||
# bitrise
|
||||
\bapp\.bitrise\.io/app/[0-9a-f]*/[\w.?=&]*
|
||||
|
||||
# bootstrapcdn.com
|
||||
\bbootstrapcdn\.com/[-./\w]+
|
||||
|
||||
# cdn.cloudflare.com
|
||||
\bcdnjs\.cloudflare\.com/[./\w]+
|
||||
|
||||
# circleci
|
||||
\bcircleci\.com/gh(?:/[^/\s"]+){1,5}.[a-z]+\?[-0-9a-zA-Z=&]+
|
||||
|
||||
# gitter
|
||||
\bgitter\.im(?:/[^/\s"]+){2}\?at=[0-9a-f]+
|
||||
|
||||
# gravatar
|
||||
\bgravatar\.com/avatar/[0-9a-f]+
|
||||
|
||||
# ibm
|
||||
[a-z.]*ibm\.com/[-_#=:%!?~.\\/\d\w]*
|
||||
|
||||
# imgur
|
||||
\bimgur\.com/[^.]+
|
||||
|
||||
# Internet Archive
|
||||
\barchive\.org/web/\d+/(?:[-\w.?,'/\\+&%$#_:]*)
|
||||
|
||||
# discord
|
||||
/discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,}
|
||||
|
||||
# Disqus
|
||||
\bdisqus\.com/[-\w/%.()!?&=_]*
|
||||
|
||||
# medium link
|
||||
\blink\.medium\.com/[a-zA-Z0-9]+
|
||||
# medium
|
||||
\bmedium\.com/\@?[^/\s"]+/[-\w]+
|
||||
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
|
||||
# powerbi
|
||||
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
|
||||
# vs devops
|
||||
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
|
||||
# microsoft store
|
||||
\bmicrosoft\.com/store/apps/\w+
|
||||
|
||||
# mvnrepository.com
|
||||
\bmvnrepository\.com/[-0-9a-z./]+
|
||||
|
||||
# now.sh
|
||||
/[0-9a-z-.]+\.now\.sh\b
|
||||
|
||||
# oracle
|
||||
\bdocs\.oracle\.com/[-0-9a-zA-Z./_?#&=]*
|
||||
|
||||
# chromatic.com
|
||||
/\S+.chromatic.com\S*[")]
|
||||
|
||||
# codacy
|
||||
\bapi\.codacy\.com/project/badge/Grade/[0-9a-f]+
|
||||
|
||||
# compai
|
||||
\bcompai\.pub/v1/png/[0-9a-f]+
|
||||
|
||||
# mailgun api
|
||||
\.api\.mailgun\.net/v3/domains/[0-9a-z]+\.mailgun.org/messages/[0-9a-zA-Z=@]*
|
||||
# mailgun
|
||||
\b[0-9a-z]+.mailgun.org
|
||||
|
||||
# /message-id/
|
||||
/message-id/[-\w@./%]+
|
||||
|
||||
# Reddit
|
||||
\breddit\.com/r/[/\w_]*
|
||||
|
||||
# requestb.in
|
||||
\brequestb\.in/[0-9a-z]+
|
||||
|
||||
# sched
|
||||
\b[a-z0-9]+\.sched\.com\b
|
||||
|
||||
# Slack url
|
||||
slack://[a-zA-Z0-9?&=]+
|
||||
# Slack
|
||||
\bslack\.com/[-0-9a-zA-Z/_~?&=.]*
|
||||
# Slack edge
|
||||
\bslack-edge\.com/[-a-zA-Z0-9?&=%./]+
|
||||
# Slack images
|
||||
\bslack-imgs\.com/[-a-zA-Z0-9?&=%.]+
|
||||
|
||||
# shields.io
|
||||
\bshields\.io/[-\w/%?=&.:+;,]*
|
||||
|
||||
# stackexchange -- https://stackexchange.com/feeds/sites
|
||||
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
|
||||
|
||||
# Sentry
|
||||
[0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b
|
||||
|
||||
# Twitter markdown
|
||||
\[\@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\)
|
||||
# Twitter hashtag
|
||||
\btwitter\.com/hashtag/[\w?_=&]*
|
||||
# Twitter status
|
||||
\btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)
|
||||
# Twitter profile images
|
||||
\btwimg\.com/profile_images/[_\w./]*
|
||||
# Twitter media
|
||||
\btwimg\.com/media/[-_\w./?=]*
|
||||
# Twitter link shortened
|
||||
\bt\.co/\w+
|
||||
|
||||
# facebook
|
||||
\bfburl\.com/[0-9a-z_]+
|
||||
# facebook CDN
|
||||
\bfbcdn\.net/[\w/.,]*
|
||||
# facebook watch
|
||||
\bfb\.watch/[0-9A-Za-z]+
|
||||
|
||||
# dropbox
|
||||
\bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+
|
||||
|
||||
# ipfs protocol
|
||||
ipfs://[0-9a-z]*
|
||||
# ipfs url
|
||||
/ipfs/[0-9a-z]*
|
||||
|
||||
# w3
|
||||
\bw3\.org/[-0-9a-zA-Z/#.]+
|
||||
|
||||
# loom
|
||||
\bloom\.com/embed/[0-9a-f]+
|
||||
|
||||
# regex101
|
||||
\bregex101\.com/r/[^/\s"]+/\d+
|
||||
|
||||
# figma
|
||||
\bfigma\.com/file(?:/[0-9a-zA-Z]+/)+
|
||||
|
||||
# freecodecamp.org
|
||||
\bfreecodecamp\.org/[-\w/.]+
|
||||
|
||||
# image.tmdb.org
|
||||
\bimage\.tmdb\.org/[/\w.]+
|
||||
|
||||
# mermaid
|
||||
\bmermaid\.ink/img/[-\w]+|\bmermaid-js\.github\.io/mermaid-live-editor/#/edit/[-\w]+
|
||||
|
||||
# Wikipedia
|
||||
\ben\.wikipedia\.org/wiki/[-\w%.#]+
|
||||
|
||||
# gitweb
|
||||
[^"\s]+/gitweb/\S+;h=[0-9a-f]+
|
||||
|
||||
# HyperKitty lists
|
||||
/archives/list/[^@/]+\@[^/\s"]*/message/[^/\s"]*/
|
||||
|
||||
# lists
|
||||
/thread\.html/[^"\s]+
|
||||
|
||||
# list-management
|
||||
\blist-manage\.com/subscribe(?:[?&](?:u|id)=[0-9a-f]+)+
|
||||
|
||||
# kubectl.kubernetes.io/last-applied-configuration
|
||||
"kubectl.kubernetes.io/last-applied-configuration": ".*"
|
||||
|
||||
# pgp
|
||||
\bgnupg\.net/pks/lookup[?&=0-9a-zA-Z]*
|
||||
|
||||
# Spotify
|
||||
\bopen\.spotify\.com/embed/playlist/\w+
|
||||
|
||||
# Mastodon
|
||||
\bmastodon\.[-a-z.]*/(?:media/|\@)[?&=0-9a-zA-Z_]*
|
||||
|
||||
# scastie
|
||||
\bscastie\.scala-lang\.org/[^/]+/\w+
|
||||
|
||||
# images.unsplash.com
|
||||
\bimages\.unsplash\.com/(?:(?:flagged|reserve)/|)[-\w./%?=%&.;]+
|
||||
|
||||
# pastebin
|
||||
\bpastebin\.com/[\w/]+
|
||||
|
||||
# heroku
|
||||
\b\w+\.heroku\.com/source/archive/\w+
|
||||
|
||||
# quip
|
||||
\b\w+\.quip\.com/\w+(?:(?:#|/issues/)\w+)?
|
||||
|
||||
# badgen.net
|
||||
\bbadgen\.net/badge/[^")\]'\s]+
|
||||
|
||||
# statuspage.io
|
||||
\w+\.statuspage\.io\b
|
||||
|
||||
# media.giphy.com
|
||||
\bmedia\.giphy\.com/media/[^/]+/[\w.?&=]+
|
||||
|
||||
# tinyurl
|
||||
\btinyurl\.com/\w+
|
||||
|
||||
# getopts
|
||||
\bgetopts\s+(?:"[^"]+"|'[^']+')
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
|
||||
|
||||
# URL escaped characters
|
||||
\%[0-9A-F][A-F]
|
||||
# IPv6
|
||||
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
|
||||
# c99 hex digits (not the full format, just one I've seen)
|
||||
0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP]
|
||||
# Punycode
|
||||
\bxn--[-0-9a-z]+
|
||||
# sha
|
||||
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
|
||||
# sha-... -- uses a fancy capture
|
||||
(['"]|")[0-9a-f]{40,}\g{-1}
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
# hex in url queries
|
||||
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
|
||||
# ssh
|
||||
(?:ssh-\S+|-nistp256) [-a-zA-Z=;:/0-9+]{12,}
|
||||
|
||||
# PGP
|
||||
\b(?:[0-9A-F]{4} ){9}[0-9A-F]{4}\b
|
||||
# GPG keys
|
||||
\b(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}\b
|
||||
# Well known gpg keys
|
||||
.well-known/openpgpkey/[\w./]+
|
||||
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
# integrity
|
||||
integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}"
|
||||
|
||||
# https://www.gnu.org/software/groff/manual/groff.html
|
||||
# man troff content
|
||||
\\f[BCIPR]
|
||||
# '
|
||||
\\\(aq
|
||||
|
||||
# .desktop mime types
|
||||
^MimeTypes?=.*$
|
||||
# .desktop localized entries
|
||||
^[A-Z][a-z]+\[[a-z]+\]=.*$
|
||||
# Localized .desktop content
|
||||
Name\[[^\]]+\]=.*
|
||||
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# crypt
|
||||
"\$2[ayb]\$.{56}"
|
||||
|
||||
# scrypt / argon
|
||||
\$(?:scrypt|argon\d+[di]*)\$\S+
|
||||
|
||||
# Input to GitHub JSON
|
||||
content: "[-a-zA-Z=;:/0-9+]*="
|
||||
|
||||
# Python stringprefix / binaryprefix
|
||||
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
|
||||
(?<!')\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
|
||||
|
||||
# Regular expressions for (P|p)assword
|
||||
\([A-Z]\|[a-z]\)[a-z]+
|
||||
|
||||
# JavaScript regular expressions
|
||||
# javascript test regex
|
||||
/.*/[gim]*\.test\(
|
||||
# javascript match regex
|
||||
\.match\(/[^/\s"]*/[gim]*\s*
|
||||
# javascript match regex
|
||||
\.match\(/\\[b].*?/[gim]*\s*\)(?:;|$)
|
||||
# javascript regex
|
||||
^\s*/\\[b].*/[gim]*\s*(?:\)(?:;|$)|,$)
|
||||
# javascript replace regex
|
||||
\.replace\(/[^/\s"]*/[gim]*\s*,
|
||||
|
||||
# Go regular expressions
|
||||
regexp?\.MustCompile\(`[^`]*`\)
|
||||
|
||||
# sed regular expressions
|
||||
sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2}
|
||||
|
||||
# go install
|
||||
go install(?:\s+[a-z]+\.[-@\w/.]+)+
|
||||
|
||||
# kubernetes pod status lists
|
||||
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
|
||||
|
||||
# kubectl - pods in CrashLoopBackOff
|
||||
\w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+
|
||||
|
||||
# kubernetes object suffix
|
||||
-[0-9a-f]{10}-\w{5}\s
|
||||
|
||||
# posthog secrets
|
||||
posthog\.init\((['"])phc_[^"',]+\g{-1},
|
||||
|
||||
# xcode
|
||||
|
||||
# xcodeproject scenes
|
||||
(?:Controller|ID|id)="\w{3}-\w{2}-\w{3}"
|
||||
|
||||
# xcode api botches
|
||||
customObjectInstantitationMethod
|
||||
|
||||
# font awesome classes
|
||||
\.fa-[-a-z0-9]+
|
||||
|
||||
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
|
||||
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
|
||||
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
|
||||
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
|
||||
## You could manually change `(?i)X...` to use `[Xx]...`
|
||||
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
|
||||
# Lorem
|
||||
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
|
||||
|
||||
# Non-English
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
|
||||
|
||||
# French
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# latex
|
||||
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
|
||||
|
||||
# the negative lookahead here is to allow catching 'templatesz' as a misspelling
|
||||
# but to otherwise recognize a Windows path with \templates\foo.template or similar:
|
||||
\\(?:necessary|r(?:eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z])
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
# Note that the next example is no longer necessary if you are using
|
||||
# to match a string starting with a `#`, use a character-class:
|
||||
[#]backwards
|
||||
# version suffix <word>v#
|
||||
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
|
||||
# Compiler flags (Scala)
|
||||
(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
# Compiler flags
|
||||
#(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# Compiler flags (linker)
|
||||
,-B
|
||||
# curl arguments
|
||||
\b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
|
||||
# set arguments
|
||||
\bset(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
|
||||
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
|
||||
# macOS temp folders
|
||||
/var/folders/\w\w/[+\w]+/(?:T|-Caches-)/
|
||||
48
.github/actions/spelling/excludes.txt
vendored
48
.github/actions/spelling/excludes.txt
vendored
@@ -1,28 +1,39 @@
|
||||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes
|
||||
(?:(?i)\.png$)
|
||||
(?:^|/)(?i)COPYRIGHT
|
||||
(?:^|/)(?i)LICEN[CS]E
|
||||
(?:^|/)3rdparty/
|
||||
(?:^|/)dirs$
|
||||
(?:^|/)go\.mod$
|
||||
(?:^|/)go\.sum$
|
||||
(?:^|/)package-lock\.json$
|
||||
(?:^|/)package(?:-lock|)\.json$
|
||||
(?:^|/)sources(?:|\.dep)$
|
||||
SUMS$
|
||||
(?:^|/)vendor/
|
||||
\.a$
|
||||
\.ai$
|
||||
\.avi$
|
||||
\.bmp$
|
||||
\.bz2$
|
||||
\.cer$
|
||||
\.class$
|
||||
\.crl$
|
||||
\.crt$
|
||||
\.csr$
|
||||
\.dll$
|
||||
\.docx?$
|
||||
\.drawio$
|
||||
\.DS_Store$
|
||||
\.eot$
|
||||
\.eps$
|
||||
\.exe$
|
||||
\.gif$
|
||||
\.gitattributes$
|
||||
\.graffle$
|
||||
\.gz$
|
||||
\.icns$
|
||||
\.ico$
|
||||
\.jar$
|
||||
\.jks$
|
||||
\.jpeg$
|
||||
\.jpg$
|
||||
\.key$
|
||||
@@ -30,28 +41,53 @@ SUMS$
|
||||
\.lock$
|
||||
\.map$
|
||||
\.min\..
|
||||
\.mod$
|
||||
\.mp3$
|
||||
\.mp4$
|
||||
\.o$
|
||||
\.ocf$
|
||||
\.otf$
|
||||
\.pbxproj$
|
||||
\.pdf$
|
||||
\.pem$
|
||||
\.png$
|
||||
\.psd$
|
||||
\.pyc$
|
||||
\.runsettings$
|
||||
\.s$
|
||||
\.sig$
|
||||
\.so$
|
||||
\.svg$
|
||||
\.svgz$
|
||||
\.svgz?$
|
||||
\.tar$
|
||||
\.tgz$
|
||||
\.tiff?$
|
||||
\.ttf$
|
||||
\.vsdx$
|
||||
\.wav$
|
||||
\.webm$
|
||||
\.webp$
|
||||
\.woff
|
||||
\.woff2?$
|
||||
\.xcf$
|
||||
\.xls
|
||||
\.xlsx?$
|
||||
\.xpm$
|
||||
\.yml$
|
||||
\.zip$
|
||||
^\.github/actions/spelling/
|
||||
^\.github/fabricbot.json$
|
||||
^\.gitignore$
|
||||
^\Q.git-blame-ignore-revs\E$
|
||||
^\Q.github/workflows/spelling.yml\E$
|
||||
^\Qdoc/reference/windows-terminal-logo.ans\E$
|
||||
^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$
|
||||
^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$
|
||||
^\Qsrc/host/ft_host/chafa.txt\E$
|
||||
^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$
|
||||
^\XamlStyler.json$
|
||||
^build/config/
|
||||
^consolegit2gitfilters\.json$
|
||||
^dep/
|
||||
^doc/reference/master-sequence-list.csv$
|
||||
@@ -61,12 +97,14 @@ SUMS$
|
||||
^src/host/runft\.bat$
|
||||
^src/host/runut\.bat$
|
||||
^src/interactivity/onecore/BgfxEngine\.
|
||||
^src/renderer/atlas/
|
||||
^src/renderer/wddmcon/WddmConRenderer\.
|
||||
^src/terminal/adapter/ut_adapter/run\.bat$
|
||||
^src/terminal/parser/delfuzzpayload\.bat$
|
||||
^src/terminal/parser/ft_fuzzer/run\.bat$
|
||||
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
|
||||
^src/terminal/parser/ft_fuzzwrapper/run\.bat$
|
||||
^src/terminal/parser/ut_parser/Base64Test.cpp$
|
||||
^src/terminal/parser/ut_parser/run\.bat$
|
||||
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
|
||||
^src/tools/lnkd/lnkd\.bat$
|
||||
@@ -74,6 +112,6 @@ SUMS$
|
||||
^src/tools/texttests/fira\.txt$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^src/types/ut_types/UtilsTests.cpp$
|
||||
^\.github/actions/spelling/
|
||||
^\.gitignore$
|
||||
^\XamlStyler.json$
|
||||
^tools/ReleaseEngineering/ServicingPipeline.ps1$
|
||||
ignore$
|
||||
SUMS$
|
||||
|
||||
8
.github/actions/spelling/expect/alphabet.txt
vendored
8
.github/actions/spelling/expect/alphabet.txt
vendored
@@ -5,26 +5,19 @@ AAAAAABBBBBBCCC
|
||||
AAAAABBBBBBCCC
|
||||
abcd
|
||||
abcd
|
||||
abcde
|
||||
abcdef
|
||||
ABCDEFG
|
||||
ABCDEFGH
|
||||
ABCDEFGHIJ
|
||||
abcdefghijk
|
||||
ABCDEFGHIJKLMNO
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
ABCG
|
||||
ABE
|
||||
abf
|
||||
BBBBB
|
||||
BBBBBBBB
|
||||
BBBBBBBBBBBBBBDDDD
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBGGRR
|
||||
CCE
|
||||
EFG
|
||||
EFGh
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
@@ -33,7 +26,6 @@ QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
|
||||
qrstuvwxyz
|
||||
qwerty
|
||||
QWERTYUIOP
|
||||
qwertyuiopasdfg
|
||||
YYYYYYYDDDDDDDDDDD
|
||||
ZAAZZ
|
||||
|
||||
915
.github/actions/spelling/expect/expect.txt
vendored
915
.github/actions/spelling/expect/expect.txt
vendored
File diff suppressed because it is too large
Load Diff
12
.github/actions/spelling/expect/web.txt
vendored
12
.github/actions/spelling/expect/web.txt
vendored
@@ -1,16 +1,6 @@
|
||||
http
|
||||
www
|
||||
ecma
|
||||
rapidtables
|
||||
WCAG
|
||||
freedesktop
|
||||
ycombinator
|
||||
robertelder
|
||||
kovidgoyal
|
||||
leonerd
|
||||
fixterms
|
||||
winui
|
||||
appshellintegration
|
||||
cppreference
|
||||
mdtauk
|
||||
gfycat
|
||||
Guake
|
||||
|
||||
62
.github/actions/spelling/line_forbidden.patterns
vendored
Normal file
62
.github/actions/spelling/line_forbidden.patterns
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
# reject `m_data` as there's a certain OS which has evil defines that break things if it's used elsewhere
|
||||
# \bm_data\b
|
||||
|
||||
# If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test,
|
||||
# you might not want to check in code where you were debugging w/ `fit()`, in which case, you might want
|
||||
# to use this:
|
||||
#\bfit\(
|
||||
|
||||
# s.b. GitHub
|
||||
\bGithub\b
|
||||
|
||||
# s.b. GitLab
|
||||
\bGitlab\b
|
||||
|
||||
# s.b. JavaScript
|
||||
\bJavascript\b
|
||||
|
||||
# s.b. Microsoft
|
||||
\bMicroSoft\b
|
||||
|
||||
# s.b. another
|
||||
\ban[- ]other\b
|
||||
|
||||
# s.b. greater than
|
||||
\bgreater then\b
|
||||
|
||||
# s.b. into
|
||||
#\sin to\s
|
||||
|
||||
# s.b. opt-in
|
||||
\sopt in\s
|
||||
|
||||
# s.b. less than
|
||||
\bless then\b
|
||||
|
||||
# s.b. otherwise
|
||||
\bother[- ]wise\b
|
||||
|
||||
# s.b. nonexistent
|
||||
\bnon existing\b
|
||||
\b[Nn]o[nt][- ]existent\b
|
||||
|
||||
# s.b. preexisting
|
||||
[Pp]re[- ]existing
|
||||
|
||||
# s.b. preempt
|
||||
[Pp]re[- ]empt\b
|
||||
|
||||
# s.b. preemptively
|
||||
[Pp]re[- ]emptively
|
||||
|
||||
# s.b. reentrancy
|
||||
[Rr]e[- ]entrancy
|
||||
|
||||
# s.b. reentrant
|
||||
[Rr]e[- ]entrant
|
||||
|
||||
# s.b. workaround(s)
|
||||
#\bwork[- ]arounds?\b
|
||||
|
||||
# Reject duplicate words
|
||||
\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s
|
||||
86
.github/actions/spelling/patterns/patterns.txt
vendored
86
.github/actions/spelling/patterns/patterns.txt
vendored
@@ -1,11 +1,6 @@
|
||||
https://(?:(?:[-a-zA-Z0-9?&=]*\.|)microsoft\.com)/[-a-zA-Z0-9?&=_#\/.]*
|
||||
https://aka\.ms/[-a-zA-Z0-9?&=\/_]*
|
||||
https://www\.itscj\.ipsj\.or\.jp/iso-ir/[-0-9]+\.pdf
|
||||
https://www\.vt100\.net/docs/[-a-zA-Z0-9#_\/.]*
|
||||
https://www.w3.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
https://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]*
|
||||
https://(?:[a-z-]+\.|)github(?:usercontent|)\.com/[-a-zA-Z0-9?%&=_\/.]*
|
||||
https://www.xfree86.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
|
||||
|
||||
https?://\S+
|
||||
[Pp]ublicKeyToken="?[0-9a-fA-F]{16}"?
|
||||
(?:[{"]|UniqueIdentifier>)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|</UniqueIdentifier)
|
||||
(?:0[Xx]|\\x|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]{0,2}\b
|
||||
@@ -24,3 +19,78 @@ VERIFY_ARE_EQUAL\(L"[^"]+"
|
||||
std::memory_order_[\w]+
|
||||
D2DERR_SHADER_COMPILE_FAILED
|
||||
TIL_FEATURE_[0-9A-Z_]+
|
||||
vcvars\w*
|
||||
ROY\sG\.\sBIV
|
||||
!(?:(?i)ESC)!\[
|
||||
!(?:(?i)CSI)!(?:\d+(?:;\d+|)m|[ABCDF])
|
||||
|
||||
# Python stringprefix / binaryprefix
|
||||
\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'
|
||||
|
||||
# Automatically suggested patterns
|
||||
# hit-count: 3831 file-count: 582
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# hit-count: 71 file-count: 35
|
||||
# Compiler flags
|
||||
(?:^|[\t ,"'`=(])-[D](?=[A-Z]{2,}|[A-Z][a-z])
|
||||
(?:^|[\t ,"'`=(])-[X](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# hit-count: 41 file-count: 28
|
||||
# version suffix <word>v#
|
||||
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
|
||||
|
||||
# hit-count: 20 file-count: 9
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
|
||||
# hit-count: 10 file-count: 7
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
|
||||
# hit-count: 4 file-count: 4
|
||||
# mailto urls
|
||||
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
|
||||
|
||||
# hit-count: 4 file-count: 1
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
|
||||
|
||||
# hit-count: 2 file-count: 1
|
||||
# latex
|
||||
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# Non-English
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# French
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# acceptable duplicates
|
||||
# ls directory listings
|
||||
[-bcdlpsw](?:[-r][-w][-sx]){3}\s+\d+\s+(\S+)\s+\g{-1}\s+\d+\s+
|
||||
# C/idl types + English ...
|
||||
\s(Guid|long|LONG|that) \g{-1}\s
|
||||
|
||||
# javadoc / .net
|
||||
(?:[\\@](?:groupname|param)|(?:public|private)(?:\s+static|\s+readonly)*)\s+(\w+)\s+\g{-1}\s
|
||||
|
||||
# Commit message -- Signed-off-by and friends
|
||||
^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$
|
||||
|
||||
# Autogenerated revert commit message
|
||||
^This reverts commit [0-9a-f]{40}\.$
|
||||
|
||||
# vtmode
|
||||
--vtmode\s+(\w+)\s+\g{-1}\s
|
||||
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
|
||||
28
.github/actions/spelling/reject.txt
vendored
28
.github/actions/spelling/reject.txt
vendored
@@ -1,22 +1,12 @@
|
||||
^attache$
|
||||
^attacher$
|
||||
^attachers$
|
||||
^spae$
|
||||
^spaebook$
|
||||
^spaecraft$
|
||||
^spaed$
|
||||
^spaedom$
|
||||
^spaeing$
|
||||
^spaeings$
|
||||
^spae-man$
|
||||
^spaeman$
|
||||
^spaer$
|
||||
^Spaerobee$
|
||||
^spaes$
|
||||
^spaewife$
|
||||
^spaewoman$
|
||||
^spaework$
|
||||
^spaewright$
|
||||
^wether$
|
||||
^wethers$
|
||||
^wetherteg$
|
||||
benefitting
|
||||
occurences?
|
||||
^dependan.*
|
||||
^oer$
|
||||
Sorce
|
||||
^[Ss]pae.*
|
||||
^untill$
|
||||
^untilling$
|
||||
^wether.*
|
||||
|
||||
132
.github/workflows/spelling2.yml
vendored
132
.github/workflows/spelling2.yml
vendored
@@ -1,20 +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:
|
||||
pull_request_target:
|
||||
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: checkout-merge
|
||||
if: "contains(github.event_name, 'pull_request')"
|
||||
uses: actions/checkout@v2
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
ref: refs/pull/${{github.event.pull_request.number}}/merge
|
||||
- name: checkout
|
||||
if: "!contains(github.event_name, 'pull_request')"
|
||||
uses: actions/checkout@v2
|
||||
- uses: check-spelling/check-spelling@v0.0.19
|
||||
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 }}
|
||||
|
||||
@@ -21,7 +21,7 @@ Write-Host "Checking test results..."
|
||||
$queryUri = GetQueryTestRunsUri -CollectionUri $CollectionUri -TeamProject $TeamProject -BuildUri $BuildUri -IncludeRunDetails
|
||||
Write-Host "queryUri = $queryUri"
|
||||
|
||||
$testRuns = Invoke-RestMethod -Uri $queryUri -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
|
||||
[System.Collections.Generic.List[string]]$failingTests = @()
|
||||
[System.Collections.Generic.List[string]]$unreliableTests = @()
|
||||
[System.Collections.Generic.List[string]]$unexpectedResultTest = @()
|
||||
@@ -50,7 +50,7 @@ foreach ($testRun in ($testRuns.value | Sort-Object -Property "completedDate" -D
|
||||
$totalTestsExecutedCount += $testRun.totalTests
|
||||
|
||||
$testRunResultsUri = "$($testRun.url)/results?api-version=5.0"
|
||||
$testResults = Invoke-RestMethod -Uri "$($testRun.url)/results?api-version=5.0" -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
$testResults = Invoke-RestMethodWithRetries "$($testRun.url)/results?api-version=5.0" -Headers $azureDevOpsRestApiHeaders
|
||||
|
||||
foreach ($testResult in $testResults.value)
|
||||
{
|
||||
|
||||
@@ -20,13 +20,31 @@ function Generate-File-Links
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<ul>"
|
||||
foreach($file in $files)
|
||||
{
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<li><a href=$($file.Link)>$($file.Name)</a></li>"
|
||||
$url = Append-HelixAccessTokenToUrl $file.Link "{Your-Helix-Access-Token-Here}"
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "<li>$($url)</li>"
|
||||
}
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "</ul>"
|
||||
Out-File -FilePath $helixLinkFile -Append -InputObject "</div>"
|
||||
}
|
||||
}
|
||||
|
||||
function Append-HelixAccessTokenToUrl
|
||||
{
|
||||
Param ([string]$url, [string]$token)
|
||||
if($token)
|
||||
{
|
||||
if($url.Contains("?"))
|
||||
{
|
||||
$url = "$($url)&access_token=$($token)"
|
||||
}
|
||||
else
|
||||
{
|
||||
$url = "$($url)?access_token=$($token)"
|
||||
}
|
||||
}
|
||||
return $url
|
||||
}
|
||||
|
||||
#Create output directory
|
||||
New-Item $OutputFolder -ItemType Directory
|
||||
|
||||
@@ -63,7 +81,8 @@ foreach ($testRun in $testRuns.value)
|
||||
if (-not $workItems.Contains($workItem))
|
||||
{
|
||||
$workItems.Add($workItem)
|
||||
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files$accessTokenParam"
|
||||
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files"
|
||||
$filesQueryUri = Append-HelixAccessTokenToUrl $filesQueryUri $helixAccessToken
|
||||
$files = Invoke-RestMethodWithRetries $filesQueryUri
|
||||
|
||||
$screenShots = $files | where { $_.Name.EndsWith(".jpg") }
|
||||
@@ -102,6 +121,7 @@ foreach ($testRun in $testRuns.value)
|
||||
|
||||
Write-Host "Downloading $link to $destination"
|
||||
|
||||
$link = Append-HelixAccessTokenToUrl $link $HelixAccessToken
|
||||
Download-FileWithRetries $link $destination
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ Write-Host "queryUri = $queryUri"
|
||||
# To account for unreliable tests, we'll iterate through all of the tests associated with this build, check to see any tests that were unreliable
|
||||
# (denoted by being marked as "skipped"), and if so, we'll instead mark those tests with a warning and enumerate all of the attempted runs
|
||||
# with their pass/fail states as well as any relevant error messages for failed attempts.
|
||||
$testRuns = Invoke-RestMethod -Uri $queryUri -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
|
||||
|
||||
$timesSeenByRunName = @{}
|
||||
|
||||
@@ -32,10 +32,10 @@ foreach ($testRun in $testRuns.value)
|
||||
$testRunResultsUri = "$($testRun.url)/results?api-version=5.0"
|
||||
|
||||
Write-Host "Marking test run `"$($testRun.name)`" as in progress so we can change its results to account for unreliable tests."
|
||||
Invoke-RestMethod -Uri "$($testRun.url)?api-version=5.0" -Method Patch -Body (ConvertTo-Json @{ "state" = "InProgress" }) -Headers $azureDevOpsRestApiHeaders -ContentType "application/json" | Out-Null
|
||||
Invoke-RestMethod "$($testRun.url)?api-version=5.0" -Method Patch -Body (ConvertTo-Json @{ "state" = "InProgress" }) -Headers $azureDevOpsRestApiHeaders -ContentType "application/json" | Out-Null
|
||||
|
||||
Write-Host "Retrieving test results..."
|
||||
$testResults = Invoke-RestMethod -Uri $testRunResultsUri -Method Get -Headers $azureDevOpsRestApiHeaders
|
||||
$testResults = Invoke-RestMethodWithRetries $testRunResultsUri -Headers $azureDevOpsRestApiHeaders
|
||||
|
||||
foreach ($testResult in $testResults.value)
|
||||
{
|
||||
@@ -54,7 +54,8 @@ foreach ($testRun in $testRuns.value)
|
||||
Write-Host " Test $($testResult.testCaseTitle) was detected as unreliable. Updating..."
|
||||
|
||||
# The errorMessage field contains a link to the JSON-encoded rerun result data.
|
||||
$rerunResults = ConvertFrom-Json (New-Object System.Net.WebClient).DownloadString($testResult.errorMessage)
|
||||
$resultsJson = Download-StringWithRetries "Error results" $testResult.errorMessage
|
||||
$rerunResults = ConvertFrom-Json $resultsJson
|
||||
[System.Collections.Generic.List[System.Collections.Hashtable]]$rerunDataList = @()
|
||||
$attemptCount = 0
|
||||
$passCount = 0
|
||||
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /t:Terminal\CascadiaPackage;Terminal\WindowsTerminalUniversal /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /t:Terminal\CascadiaPackage;Terminal\WindowsTerminalUniversal /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: true
|
||||
@@ -194,7 +194,7 @@ jobs:
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
- task: PowerShell@2
|
||||
|
||||
@@ -3,7 +3,7 @@ parameters:
|
||||
platform: ''
|
||||
additionalBuildArguments: ''
|
||||
minimumExpectedTestsExecutedCount: 1 # Sanity check for minimum expected tests to be reported
|
||||
rerunPassesRequiredToAvoidFailure: 0
|
||||
rerunPassesRequiredToAvoidFailure: 5
|
||||
|
||||
jobs:
|
||||
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
clean: true
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Code Formattting Check'
|
||||
displayName: 'Code Formatting Check'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: '.\build\scripts\Invoke-FormattingCheck.ps1'
|
||||
|
||||
@@ -22,6 +22,7 @@ jobs:
|
||||
condition: succeededOrFailed()
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
HelixAccessToken: $(HelixApiAccessToken)
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\Helix\UpdateUnreliableTests.ps1
|
||||
@@ -32,6 +33,7 @@ jobs:
|
||||
condition: succeededOrFailed()
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
HelixAccessToken: $(HelixApiAccessToken)
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\Helix\OutputTestResults.ps1
|
||||
|
||||
@@ -15,6 +15,7 @@ parameters:
|
||||
# if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline:
|
||||
useBuildOutputFromPipeline: $(System.DefinitionId)
|
||||
openHelixTargetQueues: 'windows.10.amd64.client19h1.open.xaml'
|
||||
closedHelixTargetQueues: 'windows.10.amd64.client19h1.xaml'
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
@@ -29,11 +30,11 @@ jobs:
|
||||
buildConfiguration: ${{ parameters.configuration }}
|
||||
buildPlatform: ${{ parameters.platform }}
|
||||
openHelixTargetQueues: ${{ parameters.openHelixTargetQueues }}
|
||||
closedHelixTargetQueues: ${{ parameters.closedHelixTargetQueues }}
|
||||
artifactsDir: $(Build.SourcesDirectory)\Artifacts
|
||||
taefPath: $(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.60.210621002\build\Binaries\$(buildPlatform)
|
||||
helixCommonArgs: '/binaryLogger:$(Build.SourcesDirectory)/${{parameters.name}}.$(buildPlatform).$(buildConfiguration).binlog /p:HelixBuild=$(Build.BuildId).$(buildPlatform).$(buildConfiguration) /p:Platform=$(buildPlatform) /p:Configuration=$(buildConfiguration) /p:HelixType=${{parameters.helixType}} /p:TestSuite=${{parameters.testSuite}} /p:ProjFilesPath=$(Build.ArtifactStagingDirectory) /p:rerunPassesRequiredToAvoidFailure=${{parameters.rerunPassesRequiredToAvoidFailure}}'
|
||||
|
||||
|
||||
steps:
|
||||
- task: CmdLine@1
|
||||
displayName: 'Display build machine environment variables'
|
||||
@@ -140,6 +141,7 @@ jobs:
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Run tests in Helix (open queues)'
|
||||
condition: and(succeeded(),eq(variables['System.CollectionUri'],'https://dev.azure.com/ms/'))
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
inputs:
|
||||
@@ -147,3 +149,15 @@ jobs:
|
||||
projects: build\Helix\RunTestsInHelix.proj
|
||||
custom: msbuild
|
||||
arguments: '$(helixCommonArgs) /p:IsExternal=true /p:Creator=Terminal /p:HelixTargetQueues=$(openHelixTargetQueues)'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Run tests in Helix (closed queues)'
|
||||
condition: and(succeeded(),ne(variables['System.CollectionUri'],'https://dev.azure.com/ms/'))
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
HelixAccessToken: $(HelixApiAccessToken)
|
||||
inputs:
|
||||
command: custom
|
||||
projects: build\Helix\RunTestsInHelix.proj
|
||||
custom: msbuild
|
||||
arguments: '$(helixCommonArgs) /p:HelixTargetQueues=$(closedHelixTargetQueues)'
|
||||
|
||||
@@ -20,11 +20,15 @@ jobs:
|
||||
inputs:
|
||||
artifactName: ${{ parameters.pgoArtifact }}
|
||||
downloadPath: $(artifactsPath)
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: 'Use NuGet 5.2.0'
|
||||
|
||||
- task: NuGetAuthenticate@0
|
||||
inputs:
|
||||
versionSpec: 5.2.0
|
||||
nuGetServiceConnections: 'Terminal Public Artifact Feed'
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: 'Use NuGet 5.8.0'
|
||||
inputs:
|
||||
versionSpec: 5.8.0
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy pgd files to NuGet build directory'
|
||||
@@ -58,5 +62,11 @@ jobs:
|
||||
displayName: 'NuGet push'
|
||||
inputs:
|
||||
command: push
|
||||
publishVstsFeed: Terminal/TerminalDependencies
|
||||
packagesToPush: $(Build.ArtifactStagingDirectory)/*.nupkg
|
||||
nuGetFeedType: external
|
||||
packagesToPush: $(Build.ArtifactStagingDirectory)/*.nupkg
|
||||
# The actual URL and PAT for this feed is configured at
|
||||
# https://microsoft.visualstudio.com/Dart/_settings/adminservices
|
||||
# This is the name of that connection
|
||||
publishFeedCredentials: 'Terminal Public Artifact Feed'
|
||||
feedsToUse: config
|
||||
nugetConfigPath: '$(Build.SourcesDirectory)/NuGet.config'
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2021</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>11</VersionMinor>
|
||||
<VersionMinor>12</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -154,6 +154,17 @@
|
||||
"description": "Sets how the background image aligns to the boundaries of the window when unfocused. Possible values: \"center\", \"left\", \"top\", \"right\", \"bottom\", \"topLeft\", \"topRight\", \"bottomLeft\", \"bottomRight\"",
|
||||
"type": "string"
|
||||
},
|
||||
"intenseTextStyle": {
|
||||
"default": "bright",
|
||||
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
|
||||
"enum": [
|
||||
"none",
|
||||
"bold",
|
||||
"bright",
|
||||
"all"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"experimental.retroTerminalEffect": {
|
||||
"description": "When set to true, enable retro terminal effects when unfocused. This is an experimental feature, and its continued existence is not guaranteed.",
|
||||
"type": "boolean"
|
||||
@@ -204,6 +215,22 @@
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"features": {
|
||||
"description": "Sets the DWrite font features for the given font. For example, { \"ss01\": 1, \"liga\":0 } will enable ss01 and disable ligatures.",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(([A-Za-z0-9]){4})$": { "type": "integer" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"axes": {
|
||||
"description": "Sets the DWrite font axes for the given font. For example, { \"wght\": 200 } will set the font weight to 200.",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^([A-Za-z]{4})$": { "type": "number" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -239,7 +266,9 @@
|
||||
"identifyWindows",
|
||||
"moveFocus",
|
||||
"movePane",
|
||||
"swapPane",
|
||||
"moveTab",
|
||||
"multipleActions",
|
||||
"newTab",
|
||||
"newWindow",
|
||||
"nextTab",
|
||||
@@ -274,6 +303,7 @@
|
||||
"toggleSplitOrientation",
|
||||
"toggleReadOnlyMode",
|
||||
"toggleShaderEffects",
|
||||
"updateSelection",
|
||||
"wt",
|
||||
"unbound"
|
||||
],
|
||||
@@ -285,7 +315,10 @@
|
||||
"right",
|
||||
"up",
|
||||
"down",
|
||||
"previous"
|
||||
"previous",
|
||||
"nextInOrder",
|
||||
"previousInOrder",
|
||||
"first"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -298,6 +331,24 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SelectionDirection": {
|
||||
"enum": [
|
||||
"left",
|
||||
"right",
|
||||
"up",
|
||||
"down"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SelectionMode": {
|
||||
"enum": [
|
||||
"char",
|
||||
"word",
|
||||
"view",
|
||||
"buffer"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MoveTabDirection": {
|
||||
"enum": [
|
||||
"forward",
|
||||
@@ -493,6 +544,23 @@
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Which tab to switch to, with the first being 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "index" ]
|
||||
},
|
||||
"MovePaneAction": {
|
||||
"description": "Arguments corresponding to a Move Pane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "movePane" },
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Which tab to move the pane to, with the first being 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,24 +577,46 @@
|
||||
"direction": {
|
||||
"$ref": "#/definitions/FocusDirection",
|
||||
"default": "left",
|
||||
"description": "The direction to move focus in, between panes. Direction can be 'previous' to move to the most recently used pane."
|
||||
"description": "The direction to move focus in, between panes. Direction can be 'previous' to move to the most recently used pane, or 'nextInOrder' or 'previousInOrder' to move to the next or previous pane."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"MovePaneAction": {
|
||||
"description": "Arguments corresponding to a Move Pane Action",
|
||||
"SwapPaneAction": {
|
||||
"description": "Arguments corresponding to a Swap Pane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "movePane" },
|
||||
"action": { "type": "string", "pattern": "swapPane" },
|
||||
"direction": {
|
||||
"$ref": "#/definitions/FocusDirection",
|
||||
"default": "left",
|
||||
"description": "The direction to move the focus pane in, swapping panes. Direction can be 'previous' to swap with the most recently used pane."
|
||||
"description": "The direction to move the focus pane in, swapping panes. Direction can be 'previous' to swap with the most recently used pane, or 'nextInOrder' or 'previousInOrder' to move to the next or previous pane."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"UpdateSelectionAction": {
|
||||
"description": "Arguments corresponding to a Update Selection Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "updateSelection" },
|
||||
"direction": {
|
||||
"$ref": "#/definitions/SelectionDirection",
|
||||
"default": "left",
|
||||
"description": "The direction to move the selection endpoint in."
|
||||
},
|
||||
"mode": {
|
||||
"$ref": "#/definitions/SelectionMode",
|
||||
"default": "cell",
|
||||
"description": "The expansion mode to move the selection endpoint by."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -777,6 +867,24 @@
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"MultipleActionsAction": {
|
||||
"description": "Arguments for the multiple actions command",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "multipleActions" },
|
||||
"actions" : {
|
||||
"$ref": "#/definitions/ShortcutAction",
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"description": "A list of other actions."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "actions" ]
|
||||
},
|
||||
"CommandPaletteAction": {
|
||||
"description": "Arguments for a commandPalette action",
|
||||
"allOf": [
|
||||
@@ -972,6 +1080,7 @@
|
||||
{ "$ref": "#/definitions/SwitchToTabAction" },
|
||||
{ "$ref": "#/definitions/MoveFocusAction" },
|
||||
{ "$ref": "#/definitions/MovePaneAction" },
|
||||
{ "$ref": "#/definitions/SwapPaneAction" },
|
||||
{ "$ref": "#/definitions/ResizePaneAction" },
|
||||
{ "$ref": "#/definitions/SendInputAction" },
|
||||
{ "$ref": "#/definitions/SplitPaneAction" },
|
||||
@@ -994,6 +1103,7 @@
|
||||
{ "$ref": "#/definitions/FocusPaneAction" },
|
||||
{ "$ref": "#/definitions/GlobalSummonAction" },
|
||||
{ "$ref": "#/definitions/QuakeModeAction" },
|
||||
{ "$ref": "#/definitions/UpdateSelectionAction" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
@@ -1182,6 +1292,21 @@
|
||||
"minimum": 0,
|
||||
"type": [ "integer", "string" ],
|
||||
"deprecated": true
|
||||
},
|
||||
"minimizeToTray": {
|
||||
"default": "false",
|
||||
"description": "When set to true, minimizing a Terminal window will no longer appear in the taskbar. Instead, a Terminal icon will appear in the system tray through which the user can access their windows.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"alwaysShowTrayIcon": {
|
||||
"default": "false",
|
||||
"description": "When set to true, the Terminal's tray icon will always be shown in the system tray.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"useAcrylicInTabRow": {
|
||||
"default": "false",
|
||||
"description": "When set to true, the tab row will have an acrylic background with 50% opacity.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"actions": {
|
||||
"description": "Properties are specific to each custom action.",
|
||||
|
||||
@@ -0,0 +1,619 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2020-11-20
|
||||
last updated: 2021-08-17
|
||||
issue id: #1032
|
||||
---
|
||||
|
||||
# Elevation Quality of Life Improvements
|
||||
|
||||
## Abstract
|
||||
|
||||
For a long time, we've been researching adding support to the Windows Terminal
|
||||
for running both unelevated and elevated (admin) tabs side-by-side, in the same
|
||||
window. However, after much research, we've determined that there isn't a safe
|
||||
way to do this without opening the Terminal up as a potential
|
||||
escalation-of-privilege vector.
|
||||
|
||||
Instead, we'll be adding a number of features to the Terminal to improve the
|
||||
user experience of working in elevated scenarios. These improvements include:
|
||||
|
||||
* A visible indicator that the Terminal window is elevated ([#1939])
|
||||
* Configuring the Terminal to always run elevated ([#632])
|
||||
* Configuring a specific profile to always open elevated ([#632])
|
||||
* Allowing new tabs, panes to be opened elevated directly from an unelevated
|
||||
window
|
||||
* Dynamic profile appearance that changes depending on if the Terminal is
|
||||
elevated or not. ([#1939], [#8311])
|
||||
|
||||
## Background
|
||||
|
||||
_This section was originally authored in the [Process Model 2.0 Spec]. Please
|
||||
refer to it there for its original context._
|
||||
|
||||
Let's presume that you're a user who wants to be able to open an elevated tab
|
||||
within an otherwise unelevated Terminal window. We call this scenario "mixed
|
||||
elevation" - the tabs within the Terminal can be running either unelevated _or_
|
||||
elevated client applications.
|
||||
|
||||
It wouldn't be terribly difficult for the unelevated Terminal to request the
|
||||
permission of the user to spawn an elevated client application. The user would
|
||||
see a UAC prompt, they'd accept, and then they'd be able to have an elevated
|
||||
shell alongside their unelevated tabs.
|
||||
|
||||
However, this creates an escalation of privilege vector. Now, there's an
|
||||
unelevated window which is connected directly to an elevated process. At this
|
||||
point, **any other unelevated application could send input to the Terminal's
|
||||
`HWND`**. This would make it possible for another unelevated process to "drive"
|
||||
the Terminal window, and send commands to the elevated client application.
|
||||
|
||||
It was initially theorized that the window/content model architecture would also
|
||||
help enable "mixed elevation". With mixed elevation, tabs could run at different
|
||||
integrity levels within the same terminal window. However, after investigation
|
||||
and research, it has become apparent that this scenario is not possible to do
|
||||
safely after all. There are numerous technical difficulties involved, and each
|
||||
with their own security risks. At the end of the day, the team wouldn't be
|
||||
comfortable shipping a mixed-elevation solution, because there's simply no way
|
||||
for us to be confident that we haven't introduced an escalation-of-privilege
|
||||
vector utilizing the Terminal. No matter how small the attack surface might be,
|
||||
we wouldn't be confident that there are _no_ vectors for an attack.
|
||||
|
||||
Some things we considered during this investigation:
|
||||
|
||||
* If a user requests a new elevated tab from an otherwise unelevated window, we
|
||||
could use UAC to create a new, elevated window process, and "move" all the
|
||||
current tabs to that window process, as well as the new elevated client. Now,
|
||||
the window process would be elevated, preventing it from input injection, and
|
||||
it would still contains all the previously existing tabs. The original window
|
||||
process could now be discarded, as the new elevated window process will
|
||||
pretend to be the original window.
|
||||
- However, it is unfortunately not possible with COM to have an elevated
|
||||
client attach to an unelevated server that's registered at runtime. Even in
|
||||
a packaged environment, the OS will reject the attempt to `CoCreateInstance`
|
||||
the content process object. this will prevent elevated windows from
|
||||
re-connecting to unelevated client processes.
|
||||
- We could theoretically build an RPC tunnel between content and window
|
||||
processes, and use the RPC connection to marshal the content process to the
|
||||
elevated window. However, then _we_ would need to be responsible for
|
||||
securing access the the RPC endpoint, and we feel even less confident doing
|
||||
that.
|
||||
- Attempts were also made to use a window-broker-content architecture, with
|
||||
the broker process having a static CLSID in the registry, and having the
|
||||
window and content processes at mixed elevation levels `CoCreateInstance`
|
||||
that broker. This however _also_ did not work across elevation levels. This
|
||||
may be due to a lack of Packaged COM support for mixed elevation levels.
|
||||
|
||||
It's also possible that the author forgot that packaged WinRT doesn't play
|
||||
nicely with creating objects in an elevated context. The Terminal has
|
||||
previously needed to manually manifest all its classes in a SxS manifest for
|
||||
Unpackaged WinRT to allow the classes to be activated, rather than relying
|
||||
on the packaged catalog. It's theoretically possible that doing that would
|
||||
have allowed the broker to be activated across integrity levels.
|
||||
|
||||
Even if this approach did end up working, we would still need to be
|
||||
responsible for securing the elevated windows so that an unelevated attacker
|
||||
couldn't hijack a content process and trigger unexpected code in the window
|
||||
process. We didn't feel confident that we could properly secure this channel
|
||||
either.
|
||||
|
||||
We also considered allowing mixed content in windows that were _originally_
|
||||
elevated. If the window is already elevated, then it can launch new unelevated
|
||||
processes. We could allow elevated windows to still create unelevated
|
||||
connections. However, we'd want to indicate per-pane what the elevation state
|
||||
of each connection is. The user would then need to keep track themselves of
|
||||
which terminal instances are elevated, and which are not.
|
||||
|
||||
This also marks a departure from the current behavior, where everything in an
|
||||
elevated window would be elevated by default. The user would need to specify for
|
||||
each thing in the elevated window that they'd want to create it elevated. Or the
|
||||
Terminal would need to provide some setting like
|
||||
`"autoElevateEverythingInAnElevatedWindow"`.
|
||||
|
||||
We cannot support mixed elevation when starting in a unelevated window.
|
||||
Therefore, it doesn't make a lot of UX sense to support it in the other
|
||||
direction. It's a cleaner UX story to just have everything in a single window at
|
||||
the same elevation level.
|
||||
|
||||
## Solution Design
|
||||
|
||||
Instead of supporting mixed elevation in the same window, we'll introduce the
|
||||
following features to the Terminal. These are meant as a way of improving the
|
||||
quality of life for users who work in mixed-elevation (or even just elevated)
|
||||
environments.
|
||||
|
||||
### Visible indicator for elevated windows
|
||||
|
||||
As requested in [#1939], it would be nice if it was easy to visibly identify if
|
||||
a Terminal window was elevated or not.
|
||||
|
||||
One easy way of doing this is by adding a simple UAC shield to the left of the
|
||||
tabs for elevated windows. This shield could be configured by the theme (see
|
||||
[#3327]). We could provide the following states:
|
||||
* Colored (the default)
|
||||
* Monochrome
|
||||
* Hidden, to hide the shield even on elevated windows. This is the current
|
||||
behavior.
|
||||
|
||||

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

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

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Potential Issues
|
||||
|
||||
This design is not 1:1 with the JSON file, so actions that don't have keys will not appear on this page. Additionally, you can't add a new action without keys with this current design.
|
||||
|
||||
You also cannot specify properties on commands (like the `newTab` command) and these will have to be added through the JSON file. Considering there are only a few of these and we're planning to iterate on this and add a Command Palette page, we were okay with this decision.
|
||||
|
||||
## Resources
|
||||
|
||||
### Footnotes
|
||||
@@ -29,8 +29,8 @@ Below is the schedule for when milestones will be included in release builds of
|
||||
| 2021-03-01 | [1.7] in Windows Terminal Preview<br>[1.6] in Windows Terminal | [Windows Terminal Preview 1.7 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-7-release/) |
|
||||
| 2021-04-14 | [1.8] in Windows Terminal Preview<br>[1.7] in Windows Terminal | [Windows Terminal Preview 1.8 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-8-release/) |
|
||||
| 2021-05-31 | [1.9] in Windows Terminal Preview<br>[1.8] in Windows Terminal | [Windows Terminal Preview 1.9 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-9-release/) |
|
||||
| 2021-07-31 | 1.10 in Windows Terminal Preview<br>[1.9] in Windows Terminal | |
|
||||
| 2021-08-30 | 1.11 in Windows Terminal Preview<br>1.10 in Windows Terminal | |
|
||||
| 2021-07-14 | [1.10] in Windows Terminal Preview<br>[1.9] in Windows Terminal | [Windows Terminal Preview 1.10 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-10-release/) |
|
||||
| 2021-08-31 | [1.11] in Windows Terminal Preview<br>[1.10] in Windows Terminal | [Windows Terminal Preview 1.11 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/) |
|
||||
| 2021-10-31 | 1.12 in Windows Terminal Preview<br>1.11 in Windows Terminal | |
|
||||
| 2021-11-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
|
||||
| 2021-12-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
|
||||
@@ -89,6 +89,8 @@ Feature Notes:
|
||||
[1.7]: https://github.com/microsoft/terminal/milestone/32
|
||||
[1.8]: https://github.com/microsoft/terminal/milestone/33
|
||||
[1.9]: https://github.com/microsoft/terminal/milestone/34
|
||||
[1.10]: https://github.com/microsoft/terminal/milestone/35
|
||||
[1.11]: https://github.com/microsoft/terminal/milestone/36
|
||||
[2.0]: https://github.com/microsoft/terminal/milestone/22
|
||||
[#1564]: https://github.com/microsoft/terminal/issues/1564
|
||||
[#6720]: https://github.com/microsoft/terminal/pull/6720
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @Austin-Lamb in December 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
@@ -12,4 +12,4 @@ That provenance file is automatically read and inventoried by Microsoft systems
|
||||
2. Take the parts you want, but leave most of it behind since it's HUGE and will bloat the repo to take it all. At the time of this writing, we only use small_vector.hpp and its dependencies as a header-only library.
|
||||
3. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in a version-specific subdirectory below this readme.
|
||||
If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage.
|
||||
4. Submit the pull.
|
||||
4. Submit the pull.
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @miniksa in January 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @miniksa in March 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @DHowett-MSFT in April 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @PankajBhojwani in September 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This was originally imported by @miniksa in March 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
|
||||
@@ -4,4 +4,4 @@ This manifest anchors our usage of rgb.txt from the X11 distribution.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -8,5 +8,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
|
||||
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code, Cascadia Mono (2106.17)
|
||||
* from microsoft/cascadia-code@fb0bce69c1c12f6c298b8bc1c1d181868f5daa9a
|
||||
* Cascadia Code, Cascadia Mono (2108.26)
|
||||
* from microsoft/cascadia-code@f91d08f703ee61cf4ae936b9700ca974de2748fe
|
||||
|
||||
@@ -95,16 +95,18 @@ bool TextAttribute::IsLegacy() const noexcept
|
||||
// - defaultFgColor: the default foreground color rgb value.
|
||||
// - defaultBgColor: the default background color rgb value.
|
||||
// - reverseScreenMode: true if the screen mode is reversed.
|
||||
// - blinkingIsFaint: true if blinking should be interpreted as faint.
|
||||
// - blinkingIsFaint: true if blinking should be interpreted as faint. (defaults to false)
|
||||
// - boldIsBright: true if "bold" should be interpreted as bright. (defaults to true)
|
||||
// Return Value:
|
||||
// - the foreground and background colors that should be displayed.
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode,
|
||||
const bool blinkingIsFaint) const noexcept
|
||||
const bool blinkingIsFaint,
|
||||
const bool boldIsBright) const noexcept
|
||||
{
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgColor, IsBold());
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgColor, boldIsBright && IsBold());
|
||||
auto bg = _background.GetColor(colorTable, defaultBgColor);
|
||||
if (IsFaint() || (IsBlinking() && blinkingIsFaint))
|
||||
{
|
||||
|
||||
@@ -68,7 +68,8 @@ public:
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode = false,
|
||||
const bool blinkingIsFaint = false) const noexcept;
|
||||
const bool blinkingIsFaint = false,
|
||||
const bool boldIsBright = true) const noexcept;
|
||||
|
||||
bool IsLeadingByte() const noexcept;
|
||||
bool IsTrailingByte() const noexcept;
|
||||
|
||||
@@ -1414,12 +1414,13 @@ const til::point TextBuffer::GetGlyphStart(const til::point pos) const
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the end of the current glyph/character. This is used for accessibility
|
||||
// - Update pos to be the end of the current glyph/character.
|
||||
// Arguments:
|
||||
// - pos - a COORD on the word you are currently on
|
||||
// - accessibilityMode - this is being used for accessibility; make the end exclusive.
|
||||
// Return Value:
|
||||
// - pos - The COORD for the last cell of the current glyph (exclusive)
|
||||
const til::point TextBuffer::GetGlyphEnd(const til::point pos) const
|
||||
const til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode) const
|
||||
{
|
||||
COORD resultPos = pos;
|
||||
|
||||
@@ -1430,7 +1431,10 @@ const til::point TextBuffer::GetGlyphEnd(const til::point pos) const
|
||||
}
|
||||
|
||||
// increment one more time to become exclusive
|
||||
bufferSize.IncrementInBounds(resultPos, true);
|
||||
if (accessibilityMode)
|
||||
{
|
||||
bufferSize.IncrementInBounds(resultPos, true);
|
||||
}
|
||||
return resultPos;
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ public:
|
||||
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
|
||||
|
||||
const til::point GetGlyphStart(const til::point pos) const;
|
||||
const til::point GetGlyphEnd(const til::point pos) const;
|
||||
const til::point GetGlyphEnd(const til::point pos, bool accessibilityMode = false) const;
|
||||
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false) const;
|
||||
bool MoveToPreviousGlyph(til::point& pos) const;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ class TextAttributeTests
|
||||
TEST_METHOD(TestTextAttributeColorGetters);
|
||||
TEST_METHOD(TestReverseDefaultColors);
|
||||
TEST_METHOD(TestRoundtripDefaultColors);
|
||||
TEST_METHOD(TestBoldAsBright);
|
||||
|
||||
std::array<COLORREF, 256> _colorTable;
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
@@ -263,3 +264,56 @@ void TextAttributeTests::TestRoundtripDefaultColors()
|
||||
// Reset the legacy default colors to white on black.
|
||||
TextAttribute::SetLegacyDefaultAttributes(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestBoldAsBright()
|
||||
{
|
||||
const COLORREF darkBlack = til::at(_colorTable, 0);
|
||||
const COLORREF brightBlack = til::at(_colorTable, 8);
|
||||
const COLORREF darkGreen = til::at(_colorTable, 2);
|
||||
|
||||
TextAttribute attr{};
|
||||
|
||||
// verify that calculated foreground/background are the same as the direct
|
||||
// values when not bold
|
||||
VERIFY_IS_FALSE(attr.IsBold());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
// with bold set, calculated foreground/background values shouldn't change for the default colors.
|
||||
attr.SetBold(true);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
attr.SetIndexedForeground(0);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
|
||||
Log::Comment(L"Foreground should be bright black when bold is bright is enabled");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
|
||||
Log::Comment(L"Foreground should be dark black when bold is bright is disabled");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
attr.SetIndexedBackground(2);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
|
||||
Log::Comment(L"background should be unaffected by 'bold is bright'");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
attr.SetBold(false);
|
||||
VERIFY_IS_FALSE(attr.IsBold());
|
||||
Log::Comment(L"when not bold, 'bold is bright' changes nothing");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
|
||||
Log::Comment(L"When set to a bright color, and bold, 'bold is bright' changes nothing");
|
||||
attr.SetBold(true);
|
||||
attr.SetIndexedForeground(8);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace SettingsModelLocalTests
|
||||
TEST_METHOD(TestMoveTabArgs);
|
||||
|
||||
TEST_METHOD(TestGetKeyBindingForAction);
|
||||
TEST_METHOD(KeybindingsWithoutVkey);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
@@ -109,24 +110,23 @@ namespace SettingsModelLocalTests
|
||||
},
|
||||
};
|
||||
|
||||
// Use the KeyChordHash and KeyChordEquality to compare if two
|
||||
// KeyChord's are the same. If you try to just directly compare them
|
||||
// with VERIFY_ARE_EQUAL, that will always fail. It'll revert to the
|
||||
// default winrt equality operator, which will compare if they point to
|
||||
// literally the same object (they won't).
|
||||
implementation::KeyChordHash hash;
|
||||
implementation::KeyChordEquality equals;
|
||||
for (const auto& tc : testCases)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(L"Testing case:\"%s\"", tc.expected.data()));
|
||||
|
||||
KeyChord expectedKeyChord{ tc.modifiers, tc.vkey, tc.scanCode };
|
||||
const auto actualString = KeyChordSerialization::ToString(expectedKeyChord);
|
||||
const auto actualString = KeyChordSerialization::ToString({ tc.modifiers, tc.vkey, tc.scanCode });
|
||||
VERIFY_ARE_EQUAL(tc.expected, actualString);
|
||||
|
||||
auto expectedVkey = tc.vkey;
|
||||
if (!expectedVkey)
|
||||
{
|
||||
expectedVkey = MapVirtualKeyW(tc.scanCode, MAPVK_VSC_TO_VK_EX);
|
||||
}
|
||||
|
||||
const auto actualKeyChord = KeyChordSerialization::FromString(actualString);
|
||||
VERIFY_ARE_EQUAL(hash(expectedKeyChord), hash(actualKeyChord));
|
||||
VERIFY_IS_TRUE(equals(expectedKeyChord, actualKeyChord));
|
||||
VERIFY_ARE_EQUAL(tc.modifiers, actualKeyChord.Modifiers());
|
||||
VERIFY_ARE_EQUAL(expectedVkey, actualKeyChord.Vkey());
|
||||
VERIFY_ARE_EQUAL(tc.scanCode, actualKeyChord.ScanCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,4 +789,15 @@ namespace SettingsModelLocalTests
|
||||
actionMap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::KeybindingsWithoutVkey()
|
||||
{
|
||||
const auto json = VerifyParseSucceeded(R"!([{"command": "quakeMode", "keys":"shift+sc(255)"}])!");
|
||||
|
||||
const auto actionMap = winrt::make_self<implementation::ActionMap>();
|
||||
actionMap->LayerJson(json);
|
||||
|
||||
const auto action = actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Shift, 0, 255 });
|
||||
VERIFY_IS_NOT_NULL(action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,11 +37,13 @@ namespace SettingsModelLocalTests
|
||||
|
||||
TEST_METHOD(TestTerminalArgsForBinding);
|
||||
|
||||
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
|
||||
TEST_METHOD(MakeSettingsForProfile);
|
||||
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
|
||||
|
||||
TEST_METHOD(TestLayerProfileOnColorScheme);
|
||||
|
||||
TEST_METHOD(TestCommandlineToTitlePromotion);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
return true;
|
||||
@@ -63,7 +65,7 @@ namespace SettingsModelLocalTests
|
||||
const std::string settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
"profiles": { "list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
@@ -82,6 +84,9 @@ namespace SettingsModelLocalTests
|
||||
"commandline": "wsl.exe"
|
||||
}
|
||||
],
|
||||
"defaults": {
|
||||
"historySize": 29
|
||||
} },
|
||||
"keybindings": [
|
||||
{ "keys": ["ctrl+a"], "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": ["ctrl+b"], "command": { "action": "splitPane", "split": "vertical", "profile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" } },
|
||||
@@ -126,10 +131,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
@@ -148,10 +153,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
@@ -170,10 +175,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
@@ -192,10 +197,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
}
|
||||
@@ -214,12 +219,21 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
if constexpr (Feature_ShowProfileDefaultsInSettings::IsEnabled())
|
||||
{
|
||||
// This action specified a command but no profile; it gets reassigned to the base profile
|
||||
VERIFY_ARE_EQUAL(settings.ProfileDefaults(), profile);
|
||||
VERIFY_ARE_EQUAL(29, termSettings.HistorySize());
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
|
||||
@@ -237,10 +251,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
|
||||
}
|
||||
@@ -257,10 +271,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
}
|
||||
@@ -278,10 +292,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
@@ -301,10 +315,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
@@ -323,10 +337,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, guid);
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
|
||||
@@ -346,10 +360,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, guid);
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
|
||||
@@ -371,10 +385,10 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, guid);
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
|
||||
@@ -382,9 +396,9 @@ namespace SettingsModelLocalTests
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalSettingsTests::MakeSettingsForProfileThatDoesntExist()
|
||||
void TerminalSettingsTests::MakeSettingsForProfile()
|
||||
{
|
||||
// Test that making settings throws when the GUID doesn't exist
|
||||
// Test that making settings generally works.
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
@@ -405,32 +419,32 @@ namespace SettingsModelLocalTests
|
||||
|
||||
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
const auto profile1 = settings.FindProfile(guid1);
|
||||
const auto profile2 = settings.FindProfile(guid2);
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid1, nullptr) };
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile1, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfile should succeed");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid2, nullptr) };
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile2, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
|
||||
VERIFY_IS_TRUE(false, L"This call to CreateWithProfile should succeed");
|
||||
}
|
||||
|
||||
VERIFY_THROWS(auto terminalSettings = TerminalSettings::CreateWithProfileByID(settings, guid3, nullptr), wil::ResultException, L"This call to constructor should fail");
|
||||
|
||||
try
|
||||
{
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
|
||||
@@ -554,4 +568,85 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x45, 0x67, 0x89), terminalSettings4->CursorColor()); // from profile (no color scheme)
|
||||
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings5->CursorColor()); // default
|
||||
}
|
||||
|
||||
void TerminalSettingsTests::TestCommandlineToTitlePromotion()
|
||||
{
|
||||
const std::string settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": { "list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1,
|
||||
"commandline": "cmd.exe"
|
||||
},
|
||||
],
|
||||
"defaults": {
|
||||
"historySize": 29
|
||||
} }
|
||||
})" };
|
||||
|
||||
const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
|
||||
{ // just a profile (profile wins)
|
||||
NewTerminalArgs args{};
|
||||
args.Profile(L"profile0");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // profile and command line -> no promotion (profile wins)
|
||||
NewTerminalArgs args{};
|
||||
args.Profile(L"profile0");
|
||||
args.Commandline(L"foo.exe");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // just a title -> it is propagated
|
||||
NewTerminalArgs args{};
|
||||
args.TabTitle(L"Analog Kid");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"Analog Kid", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // title and command line -> no promotion
|
||||
NewTerminalArgs args{};
|
||||
args.TabTitle(L"Digital Man");
|
||||
args.Commandline(L"foo.exe");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"Digital Man", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // just a commandline -> promotion
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"foo.exe");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
// various typesof commandline follow
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"foo.exe bar");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"\"foo exe.exe\" bar");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"foo exe.exe", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"\"\" grand designs");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L" imagine a man");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(ParseComboCommandlineIntoArgs);
|
||||
TEST_METHOD(ParseFocusTabArgs);
|
||||
TEST_METHOD(ParseMoveFocusArgs);
|
||||
TEST_METHOD(ParseMovePaneArgs);
|
||||
TEST_METHOD(ParseSwapPaneArgs);
|
||||
TEST_METHOD(ParseArgumentsWithParsingTerminators);
|
||||
TEST_METHOD(ParseFocusPaneArgs);
|
||||
|
||||
@@ -1208,14 +1208,9 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::ParseMovePaneArgs()
|
||||
void CommandlineTest::ParseSwapPaneArgs()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:useShortForm", L"{false, true}")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
INIT_TEST_PROPERTY(bool, useShortForm, L"If true, use `mp` instead of `move-pane`");
|
||||
const wchar_t* subcommand = useShortForm ? L"mp" : L"move-pane";
|
||||
const wchar_t* subcommand = L"swap-pane";
|
||||
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
@@ -1236,9 +1231,9 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Left, myArgs.Direction());
|
||||
}
|
||||
@@ -1253,9 +1248,9 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Right, myArgs.Direction());
|
||||
}
|
||||
@@ -1270,9 +1265,9 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Up, myArgs.Direction());
|
||||
}
|
||||
@@ -1287,9 +1282,9 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Down, myArgs.Direction());
|
||||
}
|
||||
@@ -1311,16 +1306,16 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
|
||||
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Left, myArgs.Direction());
|
||||
|
||||
actionAndArgs = appArgs._startupActions.at(2);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
|
||||
myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(myArgs);
|
||||
VERIFY_ARE_EQUAL(FocusDirection::Right, myArgs.Direction());
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(MoveFocusFromZoomedPane);
|
||||
TEST_METHOD(CloseZoomedPane);
|
||||
|
||||
TEST_METHOD(MovePanes);
|
||||
TEST_METHOD(SwapPanes);
|
||||
|
||||
TEST_METHOD(NextMRUTab);
|
||||
TEST_METHOD(VerifyCommandPaletteTabSwitcherOrder);
|
||||
@@ -430,7 +430,7 @@ namespace TerminalAppLocalTests
|
||||
Log::Comment(L"Duplicate the tab, and don't crash");
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_DuplicateFocusedTab();
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
VERIFY_ARE_EQUAL(3u, page->_tabs.Size(), L"We should successfully duplicate a tab hosting a deleted profile.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
@@ -530,9 +530,9 @@ namespace TerminalAppLocalTests
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(2,
|
||||
VERIFY_ARE_EQUAL(3,
|
||||
tab->GetLeafPaneCount(),
|
||||
L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
L"We should successfully duplicate a pane hosting a deleted profile.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
|
||||
@@ -821,7 +821,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
void TabTests::MovePanes()
|
||||
void TabTests::SwapPanes()
|
||||
{
|
||||
auto page = _commonSetup();
|
||||
|
||||
@@ -914,10 +914,10 @@ namespace TerminalAppLocalTests
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
MovePaneArgs args{ FocusDirection::Left };
|
||||
SwapPaneArgs args{ FocusDirection::Left };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleMovePane(nullptr, eventArgs);
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
@@ -945,10 +945,10 @@ namespace TerminalAppLocalTests
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
MovePaneArgs args{ FocusDirection::Up };
|
||||
SwapPaneArgs args{ FocusDirection::Up };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleMovePane(nullptr, eventArgs);
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
@@ -976,10 +976,10 @@ namespace TerminalAppLocalTests
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
MovePaneArgs args{ FocusDirection::Right };
|
||||
SwapPaneArgs args{ FocusDirection::Right };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleMovePane(nullptr, eventArgs);
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
@@ -1007,10 +1007,10 @@ namespace TerminalAppLocalTests
|
||||
// -------------------
|
||||
TestOnUIThread([&]() {
|
||||
// Set up action
|
||||
MovePaneArgs args{ FocusDirection::Down };
|
||||
SwapPaneArgs args{ FocusDirection::Down };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
|
||||
page->_HandleMovePane(nullptr, eventArgs);
|
||||
page->_HandleSwapPane(nullptr, eventArgs);
|
||||
});
|
||||
|
||||
Sleep(250);
|
||||
|
||||
@@ -549,11 +549,11 @@ try
|
||||
|
||||
if (multiClickMapper == 3)
|
||||
{
|
||||
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansionMode::Line);
|
||||
_terminal->MultiClickSelection(cursorPosition / fontSize, winrt::Microsoft::Terminal::Core::SelectionExpansion::Line);
|
||||
}
|
||||
else if (multiClickMapper == 2)
|
||||
{
|
||||
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansionMode::Word);
|
||||
_terminal->MultiClickSelection(cursorPosition / fontSize, winrt::Microsoft::Terminal::Core::SelectionExpansion::Word);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -44,6 +44,13 @@
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)src\cascadia\TerminalCore\lib\Generated Files\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
|
||||
<!-- LATE LINK LINE OVERRIDES. This project must link named forwarders
|
||||
|
||||
@@ -9,3 +9,5 @@
|
||||
#endif
|
||||
|
||||
#include <LibraryIncludes.h>
|
||||
#define CPPWINRT_VERSION "2.0.210309.3"
|
||||
#include "winrt/Microsoft.Terminal.Core.h"
|
||||
|
||||
@@ -28,8 +28,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// This is a private constructor to be used in unit tests, where we don't
|
||||
// want each Monarch to necessarily use the current PID.
|
||||
// This constructor is intended to be used in unit tests,
|
||||
// but we need to make it public in order to use make_self
|
||||
// in the tests. It's not exposed through the idl though
|
||||
// so it's not _truly_ fully public which should be acceptable.
|
||||
Monarch::Monarch(const uint64_t testPID) :
|
||||
_ourPID{ testPID }
|
||||
{
|
||||
@@ -78,6 +80,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows });
|
||||
peasant.RenameRequested({ this, &Monarch::_renameRequested });
|
||||
|
||||
peasant.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
|
||||
peasant.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
|
||||
|
||||
_peasants[newPeasantsId] = peasant;
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
@@ -738,20 +743,30 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
try
|
||||
{
|
||||
args.FoundMatch(false);
|
||||
|
||||
// If a WindowID is provided from the args, use that first.
|
||||
uint64_t windowId = 0;
|
||||
// If no name was provided, then just summon the MRU window.
|
||||
if (searchedForName.empty())
|
||||
if (args.WindowID())
|
||||
{
|
||||
// Use the value of the `desktop` arg to determine if we should
|
||||
// limit to the current desktop (desktop:onCurrent) or not
|
||||
// (desktop:any or desktop:toCurrent)
|
||||
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop(), false);
|
||||
windowId = args.WindowID().Value();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find a peasant that currently has this name
|
||||
windowId = _lookupPeasantIdForName(searchedForName);
|
||||
// If no name was provided, then just summon the MRU window.
|
||||
if (searchedForName.empty())
|
||||
{
|
||||
// Use the value of the `desktop` arg to determine if we should
|
||||
// limit to the current desktop (desktop:onCurrent) or not
|
||||
// (desktop:any or desktop:toCurrent)
|
||||
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find a peasant that currently has this name
|
||||
windowId = _lookupPeasantIdForName(searchedForName);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto targetPeasant{ _getPeasant(windowId) })
|
||||
{
|
||||
targetPeasant.Summon(args.SummonBehavior());
|
||||
@@ -789,4 +804,56 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method creates a map of peasant IDs to peasant names
|
||||
// while removing dead peasants.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - A map of peasant IDs to their names.
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> Monarch::GetPeasantNames()
|
||||
{
|
||||
auto names = winrt::single_threaded_map<uint64_t, winrt::hstring>();
|
||||
|
||||
std::vector<uint64_t> peasantsToErase{};
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
try
|
||||
{
|
||||
names.Insert(id, p.WindowName());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
peasantsToErase.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the dead peasants we came across while iterating.
|
||||
for (const auto& id : peasantsToErase)
|
||||
{
|
||||
_peasants.erase(id);
|
||||
_clearOldMruEntries(id);
|
||||
}
|
||||
|
||||
return names.GetView();
|
||||
}
|
||||
|
||||
void Monarch::SummonAllWindows()
|
||||
{
|
||||
auto callback = [](auto&& p, auto&& /*id*/) {
|
||||
SummonWindowBehavior args{};
|
||||
args.ToggleVisibility(false);
|
||||
p.Summon(args);
|
||||
};
|
||||
auto onError = [](auto&& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonAll_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not summon"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
_forAllPeasantsIgnoringTheDead(callback, onError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
struct Monarch : public MonarchT<Monarch>
|
||||
{
|
||||
Monarch();
|
||||
Monarch(const uint64_t testPID);
|
||||
~Monarch();
|
||||
|
||||
uint64_t GetPID();
|
||||
@@ -51,10 +52,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
|
||||
void SummonAllWindows();
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
Monarch(const uint64_t testPID);
|
||||
uint64_t _ourPID;
|
||||
|
||||
uint64_t _nextPeasantID{ 1 };
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Microsoft.Terminal.Remoting
|
||||
|
||||
Boolean FoundMatch;
|
||||
SummonWindowBehavior SummonBehavior;
|
||||
Windows.Foundation.IReference<UInt64> WindowID;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +41,11 @@ namespace Microsoft.Terminal.Remoting
|
||||
void HandleActivatePeasant(WindowActivatedArgs args);
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
|
||||
void SummonAllWindows();
|
||||
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames { get; };
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
}
|
||||
|
||||
// This is a private constructor to be used in unit tests, where we don't
|
||||
// want each Peasant to necessarily use the current PID.
|
||||
// This constructor is intended to be used in unit tests,
|
||||
// but we need to make it public in order to use make_self
|
||||
// in the tests. It's not exposed through the idl though
|
||||
// so it's not _truly_ fully public which should be acceptable.
|
||||
Peasant::Peasant(const uint64_t testPID) :
|
||||
_ourPID{ testPID }
|
||||
{
|
||||
@@ -31,6 +33,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
uint64_t Peasant::GetID()
|
||||
{
|
||||
return _id;
|
||||
@@ -222,4 +225,36 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::RequestShowTrayIcon()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ShowTrayIconRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_RequestShowTrayIcon",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::RequestHideTrayIcon()
|
||||
{
|
||||
try
|
||||
{
|
||||
_HideTrayIconRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_RequestHideTrayIcon",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
void RequestIdentifyWindows();
|
||||
void DisplayWindowId();
|
||||
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
|
||||
|
||||
@@ -40,6 +42,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
|
||||
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
Peasant(const uint64_t testPID);
|
||||
|
||||
@@ -64,6 +64,8 @@ namespace Microsoft.Terminal.Remoting
|
||||
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
||||
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
|
||||
void Summon(SummonWindowBehavior behavior);
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
||||
@@ -71,6 +73,8 @@ namespace Microsoft.Terminal.Remoting
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Peasant : IPeasant
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
WINRT_PROPERTY(bool, FoundMatch, false);
|
||||
WINRT_PROPERTY(bool, OnCurrentDesktop, false);
|
||||
WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior);
|
||||
|
||||
WINRT_PROPERTY(Windows::Foundation::IReference<uint64_t>, WindowID);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -254,6 +254,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// window, and when the current monarch dies.
|
||||
|
||||
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
|
||||
_monarch.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
|
||||
_monarch.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
|
||||
|
||||
_BecameMonarchHandlers(*this, nullptr);
|
||||
}
|
||||
@@ -509,4 +511,57 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
_monarch.SummonWindow(args);
|
||||
}
|
||||
|
||||
void WindowManager::SummonAllWindows()
|
||||
{
|
||||
if constexpr (Feature_TrayIcon::IsEnabled())
|
||||
{
|
||||
_monarch.SummonAllWindows();
|
||||
}
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> WindowManager::GetPeasantNames()
|
||||
{
|
||||
// We should only get called when we're the monarch since the monarch
|
||||
// is the only one that knows about all peasants.
|
||||
return _monarch.GetPeasantNames();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to show a tray icon.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestShowTrayIcon()
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
_peasant.RequestShowTrayIcon();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to hide its tray icon.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestHideTrayIcon()
|
||||
{
|
||||
auto strongThis{ get_strong() };
|
||||
co_await winrt::resume_background();
|
||||
_peasant.RequestHideTrayIcon();
|
||||
}
|
||||
|
||||
bool WindowManager::DoesQuakeWindowExist()
|
||||
{
|
||||
const auto names = GetPeasantNames();
|
||||
for (const auto [id, name] : names)
|
||||
{
|
||||
if (name == QuakeWindowName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,8 +40,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
bool IsMonarch();
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
|
||||
void SummonAllWindows();
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();
|
||||
|
||||
winrt::fire_and_forget RequestShowTrayIcon();
|
||||
winrt::fire_and_forget RequestHideTrayIcon();
|
||||
bool DoesQuakeWindowExist();
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
bool _shouldCreateWindow{ false };
|
||||
|
||||
@@ -12,7 +12,14 @@ namespace Microsoft.Terminal.Remoting
|
||||
IPeasant CurrentWindow();
|
||||
Boolean IsMonarch { get; };
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
void SummonAllWindows();
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
Boolean DoesQuakeWindowExist();
|
||||
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames();
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
Color="{ThemeResource SystemErrorTextColor}" />
|
||||
|
||||
<!-- Suppress top padding -->
|
||||
<Thickness x:Key="TabViewHeaderPadding">8,0,8,0</Thickness>
|
||||
<Thickness x:Key="TabViewHeaderPadding">9,0,8,0</Thickness>
|
||||
|
||||
<!-- Remove when implementing WinUI 2.6 -->
|
||||
<Thickness x:Key="FlyoutContentPadding">12</Thickness>
|
||||
@@ -55,12 +55,18 @@
|
||||
<!-- Define resources for Dark mode here -->
|
||||
<SolidColorBrush x:Key="TabViewBackground"
|
||||
Color="#FF333333" />
|
||||
|
||||
<SolidColorBrush x:Key="UnfocusedBorderBrush"
|
||||
Color="#FF333333" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<!-- Define resources for Light mode here -->
|
||||
<SolidColorBrush x:Key="TabViewBackground"
|
||||
Color="#FFCCCCCC" />
|
||||
|
||||
<SolidColorBrush x:Key="UnfocusedBorderBrush"
|
||||
Color="#FFCCCCCC" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
@@ -143,6 +143,20 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMovePane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args == nullptr)
|
||||
{
|
||||
args.Handled(false);
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
|
||||
{
|
||||
auto moved = _MovePane(realArgs.TabIndex());
|
||||
args.Handled(moved);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSplitPane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
@@ -323,10 +337,10 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMovePane(const IInspectable& /*sender*/,
|
||||
void TerminalPage::_HandleSwapPane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SwapPaneArgs>())
|
||||
{
|
||||
if (realArgs.Direction() == FocusDirection::None)
|
||||
{
|
||||
@@ -335,8 +349,8 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
_MovePane(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
auto swapped = _SwapPane(realArgs.Direction());
|
||||
args.Handled(swapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -739,11 +753,10 @@ namespace winrt::TerminalApp::implementation
|
||||
newTerminalArgs = NewTerminalArgs();
|
||||
}
|
||||
|
||||
const auto profileGuid{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
|
||||
// Manually fill in the evaluated profile.
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profileGuid));
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
|
||||
_OpenNewWindow(false, newTerminalArgs);
|
||||
actionArgs.Handled(true);
|
||||
}
|
||||
@@ -861,4 +874,52 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleClearBuffer(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<ClearBufferArgs>())
|
||||
{
|
||||
if (const auto termControl{ _GetActiveControl() })
|
||||
{
|
||||
termControl.ClearBuffer(realArgs.Clear());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMultipleActions(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<MultipleActionsArgs>())
|
||||
{
|
||||
for (const auto& action : realArgs.Actions())
|
||||
{
|
||||
_actionDispatch->DoAction(action);
|
||||
}
|
||||
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleUpdateSelection(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<UpdateSelectionArgs>())
|
||||
{
|
||||
if (const auto termControl{ _GetActiveControl() })
|
||||
{
|
||||
args.Handled(termControl.UpdateSelection(realArgs.Direction(), realArgs.Mode()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@ void AppCommandlineArgs::_buildParser()
|
||||
_buildFocusTabParser();
|
||||
_buildMoveFocusParser();
|
||||
_buildMovePaneParser();
|
||||
_buildSwapPaneParser();
|
||||
_buildFocusPaneParser();
|
||||
}
|
||||
|
||||
@@ -297,6 +298,43 @@ void AppCommandlineArgs::_buildSplitPaneParser()
|
||||
setupSubcommand(_newPaneCommand);
|
||||
setupSubcommand(_newPaneShort);
|
||||
}
|
||||
// Method Description:
|
||||
// - Adds the `move-pane` subcommand and related options to the commandline parser.
|
||||
// - Additionally adds the `mp` subcommand, which is just a shortened version of `move-pane`
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppCommandlineArgs::_buildMovePaneParser()
|
||||
{
|
||||
_movePaneCommand = _app.add_subcommand("move-pane", RS_A(L"CmdMovePaneDesc"));
|
||||
_movePaneShort = _app.add_subcommand("mp", RS_A(L"CmdMPDesc"));
|
||||
|
||||
auto setupSubcommand = [this](auto* subcommand) {
|
||||
subcommand->add_option("-t,--tab",
|
||||
_movePaneTabIndex,
|
||||
RS_A(L"CmdMovePaneTabArgDesc"));
|
||||
|
||||
// When ParseCommand is called, if this subcommand was provided, this
|
||||
// callback function will be triggered on the same thread. We can be sure
|
||||
// that `this` will still be safe - this function just lets us know this
|
||||
// command was parsed.
|
||||
subcommand->callback([&, this]() {
|
||||
// Build the action from the values we've parsed on the commandline.
|
||||
ActionAndArgs movePaneAction{};
|
||||
|
||||
if (_movePaneTabIndex >= 0)
|
||||
{
|
||||
movePaneAction.Action(ShortcutAction::MovePane);
|
||||
MovePaneArgs args{ static_cast<unsigned int>(_movePaneTabIndex) };
|
||||
movePaneAction.Args(args);
|
||||
_startupActions.push_back(movePaneAction);
|
||||
}
|
||||
});
|
||||
};
|
||||
setupSubcommand(_movePaneCommand);
|
||||
setupSubcommand(_movePaneShort);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Adds the `focus-tab` subcommand and related options to the commandline parser.
|
||||
@@ -342,6 +380,11 @@ void AppCommandlineArgs::_buildFocusTabParser()
|
||||
else if (_focusNextTab || _focusPrevTab)
|
||||
{
|
||||
focusTabAction.Action(_focusNextTab ? ShortcutAction::NextTab : ShortcutAction::PrevTab);
|
||||
// GH#10070 - make sure to not use the MRU order when switching
|
||||
// tabs on the commandline. That wouldn't make any sense!
|
||||
focusTabAction.Args(_focusNextTab ?
|
||||
static_cast<IActionArgs>(NextTabArgs(TabSwitcherMode::Disabled)) :
|
||||
static_cast<IActionArgs>(PrevTabArgs(TabSwitcherMode::Disabled)));
|
||||
_startupActions.push_back(std::move(focusTabAction));
|
||||
}
|
||||
});
|
||||
@@ -351,6 +394,17 @@ void AppCommandlineArgs::_buildFocusTabParser()
|
||||
setupSubcommand(_focusTabShort);
|
||||
}
|
||||
|
||||
static const std::map<std::string, FocusDirection> focusDirectionMap = {
|
||||
{ "left", FocusDirection::Left },
|
||||
{ "right", FocusDirection::Right },
|
||||
{ "up", FocusDirection::Up },
|
||||
{ "down", FocusDirection::Down },
|
||||
{ "previous", FocusDirection::Previous },
|
||||
{ "nextInOrder", FocusDirection::NextInOrder },
|
||||
{ "previousInOrder", FocusDirection::PreviousInOrder },
|
||||
{ "first", FocusDirection::First },
|
||||
};
|
||||
|
||||
// Method Description:
|
||||
// - Adds the `move-focus` subcommand and related options to the commandline parser.
|
||||
// - Additionally adds the `mf` subcommand, which is just a shortened version of `move-focus`
|
||||
@@ -364,18 +418,11 @@ void AppCommandlineArgs::_buildMoveFocusParser()
|
||||
_moveFocusShort = _app.add_subcommand("mf", RS_A(L"CmdMFDesc"));
|
||||
|
||||
auto setupSubcommand = [this](auto* subcommand) {
|
||||
std::map<std::string, FocusDirection> map = {
|
||||
{ "left", FocusDirection::Left },
|
||||
{ "right", FocusDirection::Right },
|
||||
{ "up", FocusDirection::Up },
|
||||
{ "down", FocusDirection::Down }
|
||||
};
|
||||
|
||||
auto* directionOpt = subcommand->add_option("direction",
|
||||
_moveFocusDirection,
|
||||
RS_A(L"CmdMoveFocusDirectionArgDesc"));
|
||||
|
||||
directionOpt->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
|
||||
directionOpt->transform(CLI::CheckedTransformer(focusDirectionMap, CLI::ignore_case));
|
||||
directionOpt->required();
|
||||
// When ParseCommand is called, if this subcommand was provided, this
|
||||
// callback function will be triggered on the same thread. We can be sure
|
||||
@@ -400,42 +447,33 @@ void AppCommandlineArgs::_buildMoveFocusParser()
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Adds the `move-pane` subcommand and related options to the commandline parser.
|
||||
// - Additionally adds the `mp` subcommand, which is just a shortened version of `move-pane`
|
||||
// - Adds the `swap-pane` subcommand and related options to the commandline parser.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppCommandlineArgs::_buildMovePaneParser()
|
||||
void AppCommandlineArgs::_buildSwapPaneParser()
|
||||
{
|
||||
_movePaneCommand = _app.add_subcommand("move-pane", RS_A(L"CmdMovePaneDesc"));
|
||||
_movePaneShort = _app.add_subcommand("mp", RS_A(L"CmdMPDesc"));
|
||||
_swapPaneCommand = _app.add_subcommand("swap-pane", RS_A(L"CmdSwapPaneDesc"));
|
||||
|
||||
auto setupSubcommand = [this](auto* subcommand) {
|
||||
std::map<std::string, FocusDirection> map = {
|
||||
{ "left", FocusDirection::Left },
|
||||
{ "right", FocusDirection::Right },
|
||||
{ "up", FocusDirection::Up },
|
||||
{ "down", FocusDirection::Down }
|
||||
};
|
||||
|
||||
auto* directionOpt = subcommand->add_option("direction",
|
||||
_movePaneDirection,
|
||||
RS_A(L"CmdMovePaneDirectionArgDesc"));
|
||||
_swapPaneDirection,
|
||||
RS_A(L"CmdSwapPaneDirectionArgDesc"));
|
||||
|
||||
directionOpt->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
|
||||
directionOpt->transform(CLI::CheckedTransformer(focusDirectionMap, CLI::ignore_case));
|
||||
directionOpt->required();
|
||||
// When ParseCommand is called, if this subcommand was provided, this
|
||||
// callback function will be triggered on the same thread. We can be sure
|
||||
// that `this` will still be safe - this function just lets us know this
|
||||
// command was parsed.
|
||||
subcommand->callback([&, this]() {
|
||||
if (_movePaneDirection != FocusDirection::None)
|
||||
if (_swapPaneDirection != FocusDirection::None)
|
||||
{
|
||||
MovePaneArgs args{ _movePaneDirection };
|
||||
SwapPaneArgs args{ _swapPaneDirection };
|
||||
|
||||
ActionAndArgs actionAndArgs{};
|
||||
actionAndArgs.Action(ShortcutAction::MovePane);
|
||||
actionAndArgs.Action(ShortcutAction::SwapPane);
|
||||
actionAndArgs.Args(args);
|
||||
|
||||
_startupActions.push_back(std::move(actionAndArgs));
|
||||
@@ -443,8 +481,7 @@ void AppCommandlineArgs::_buildMovePaneParser()
|
||||
});
|
||||
};
|
||||
|
||||
setupSubcommand(_movePaneCommand);
|
||||
setupSubcommand(_movePaneShort);
|
||||
setupSubcommand(_swapPaneCommand);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -625,6 +662,7 @@ bool AppCommandlineArgs::_noCommandsProvided()
|
||||
*_moveFocusShort ||
|
||||
*_movePaneCommand ||
|
||||
*_movePaneShort ||
|
||||
*_swapPaneCommand ||
|
||||
*_focusPaneCommand ||
|
||||
*_focusPaneShort ||
|
||||
*_newPaneShort.subcommand ||
|
||||
@@ -653,12 +691,13 @@ void AppCommandlineArgs::_resetStateToDefault()
|
||||
_splitPaneSize = 0.5f;
|
||||
_splitDuplicate = false;
|
||||
|
||||
_movePaneTabIndex = -1;
|
||||
_focusTabIndex = -1;
|
||||
_focusNextTab = false;
|
||||
_focusPrevTab = false;
|
||||
|
||||
_moveFocusDirection = FocusDirection::None;
|
||||
_movePaneDirection = FocusDirection::None;
|
||||
_swapPaneDirection = FocusDirection::None;
|
||||
|
||||
_focusPaneTarget = -1;
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ private:
|
||||
CLI::App* _moveFocusShort;
|
||||
CLI::App* _movePaneCommand;
|
||||
CLI::App* _movePaneShort;
|
||||
CLI::App* _swapPaneCommand;
|
||||
CLI::App* _focusPaneCommand;
|
||||
CLI::App* _focusPaneShort;
|
||||
|
||||
@@ -97,7 +98,7 @@ private:
|
||||
bool _suppressApplicationTitle{ false };
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _moveFocusDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
|
||||
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _movePaneDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
|
||||
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _swapPaneDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
|
||||
|
||||
// _commandline will contain the command line with which we'll be spawning a new terminal
|
||||
std::vector<std::string> _commandline;
|
||||
@@ -107,6 +108,7 @@ private:
|
||||
bool _splitDuplicate{ false };
|
||||
float _splitPaneSize{ 0.5f };
|
||||
|
||||
int _movePaneTabIndex{ -1 };
|
||||
int _focusTabIndex{ -1 };
|
||||
bool _focusNextTab{ false };
|
||||
bool _focusPrevTab{ false };
|
||||
@@ -132,6 +134,7 @@ private:
|
||||
void _buildFocusTabParser();
|
||||
void _buildMoveFocusParser();
|
||||
void _buildMovePaneParser();
|
||||
void _buildSwapPaneParser();
|
||||
void _buildFocusPaneParser();
|
||||
bool _noCommandsProvided();
|
||||
void _resetStateToDefault();
|
||||
|
||||
@@ -1442,4 +1442,39 @@ namespace winrt::TerminalApp::implementation
|
||||
return _root->IsQuakeWindow();
|
||||
}
|
||||
|
||||
bool AppLogic::GetMinimizeToTray()
|
||||
{
|
||||
if constexpr (Feature_TrayIcon::IsEnabled())
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings.GlobalSettings().MinimizeToTray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AppLogic::GetAlwaysShowTrayIcon()
|
||||
{
|
||||
if constexpr (Feature_TrayIcon::IsEnabled())
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings.GlobalSettings().AlwaysShowTrayIcon();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,9 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
winrt::TerminalApp::TaskbarState TaskbarState();
|
||||
|
||||
bool GetMinimizeToTray();
|
||||
bool GetAlwaysShowTrayIcon();
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
|
||||
|
||||
Windows::Foundation::Collections::IMapView<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::Command> GlobalHotkeys();
|
||||
|
||||
@@ -70,6 +70,9 @@ namespace TerminalApp
|
||||
|
||||
TaskbarState TaskbarState{ get; };
|
||||
|
||||
Boolean GetMinimizeToTray();
|
||||
Boolean GetAlwaysShowTrayIcon();
|
||||
|
||||
FindTargetWindowResult FindTargetWindow(String[] args);
|
||||
|
||||
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_allCommands = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
|
||||
_tabActions = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
|
||||
_mruTabActions = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
|
||||
_commandLineHistory = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
|
||||
|
||||
_switchToMode(CommandPaletteMode::ActionMode);
|
||||
|
||||
@@ -245,6 +244,17 @@ namespace winrt::TerminalApp::implementation
|
||||
_PreviewActionHandlers(*this, actionPaletteItem.Command());
|
||||
}
|
||||
}
|
||||
else if (_currentMode == CommandPaletteMode::CommandlineMode)
|
||||
{
|
||||
if (filteredCommand)
|
||||
{
|
||||
SearchBoxPlaceholderText(filteredCommand.Item().Name());
|
||||
}
|
||||
else
|
||||
{
|
||||
SearchBoxPlaceholderText(RS_(L"CmdPalCommandlinePrompt"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandPalette::_previewKeyDownHandler(IInspectable const& /*sender*/,
|
||||
@@ -365,6 +375,17 @@ namespace winrt::TerminalApp::implementation
|
||||
_searchBox().PasteFromClipboard();
|
||||
e.Handled(true);
|
||||
}
|
||||
else if (key == VirtualKey::Right && _currentMode == CommandPaletteMode::CommandlineMode)
|
||||
{
|
||||
if (const auto command{ _filteredActionsView().SelectedItem().try_as<winrt::TerminalApp::FilteredCommand>() })
|
||||
{
|
||||
_searchBox().Text(command.Item().Name());
|
||||
_searchBox().Select(_searchBox().Text().size(), 0);
|
||||
_searchBox().Focus(FocusState::Programmatic);
|
||||
_filteredActionsView().SelectedIndex(-1);
|
||||
e.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -587,7 +608,7 @@ namespace winrt::TerminalApp::implementation
|
||||
case CommandPaletteMode::TabSwitchMode:
|
||||
return _tabSwitcherMode == TabSwitcherMode::MostRecentlyUsed ? _mruTabActions : _tabActions;
|
||||
case CommandPaletteMode::CommandlineMode:
|
||||
return _commandLineHistory;
|
||||
return _loadRecentCommands();
|
||||
default:
|
||||
return _allCommands;
|
||||
}
|
||||
@@ -720,14 +741,10 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void CommandPalette::_dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command)
|
||||
{
|
||||
const auto filteredCommand = command ? command : _buildCommandLineCommand(_getTrimmedInput());
|
||||
const auto filteredCommand = command ? command : _buildCommandLineCommand(winrt::hstring(_getTrimmedInput()));
|
||||
if (filteredCommand.has_value())
|
||||
{
|
||||
if (_commandLineHistory.Size() == CommandLineHistoryLength)
|
||||
{
|
||||
_commandLineHistory.RemoveAtEnd();
|
||||
}
|
||||
_commandLineHistory.InsertAt(0, filteredCommand.value());
|
||||
_updateRecentCommands(filteredCommand.value().Item().Name());
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
@@ -744,15 +761,14 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<winrt::TerminalApp::FilteredCommand> CommandPalette::_buildCommandLineCommand(std::wstring const& commandLine)
|
||||
std::optional<TerminalApp::FilteredCommand> CommandPalette::_buildCommandLineCommand(const hstring& commandLine)
|
||||
{
|
||||
if (commandLine.empty())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
winrt::hstring cl{ commandLine };
|
||||
auto commandLinePaletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(cl) };
|
||||
auto commandLinePaletteItem{ winrt::make<CommandLinePaletteItem>(commandLine) };
|
||||
return winrt::make<FilteredCommand>(commandLinePaletteItem);
|
||||
}
|
||||
|
||||
@@ -1217,4 +1233,81 @@ namespace winrt::TerminalApp::implementation
|
||||
itemContainer.DataContext(args.Item());
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Reads the list of recent commands from the persistent application state
|
||||
// Return Value:
|
||||
// - The list of FilteredCommand representing the ones stored in the state
|
||||
IVector<TerminalApp::FilteredCommand> CommandPalette::_loadRecentCommands()
|
||||
{
|
||||
const auto recentCommands = ApplicationState::SharedInstance().RecentCommands();
|
||||
// If this is the first time we've opened the commandline mode and
|
||||
// there aren't any recent commands, then just return an empty vector.
|
||||
if (!recentCommands)
|
||||
{
|
||||
return single_threaded_vector<TerminalApp::FilteredCommand>();
|
||||
}
|
||||
|
||||
std::vector<TerminalApp::FilteredCommand> parsedCommands;
|
||||
parsedCommands.reserve(std::min(recentCommands.Size(), CommandLineHistoryLength));
|
||||
|
||||
for (const auto& c : recentCommands)
|
||||
{
|
||||
if (parsedCommands.size() >= CommandLineHistoryLength)
|
||||
{
|
||||
// Don't load more than CommandLineHistoryLength commands
|
||||
break;
|
||||
}
|
||||
|
||||
if (const auto parsedCommand = _buildCommandLineCommand(c))
|
||||
{
|
||||
parsedCommands.push_back(*parsedCommand);
|
||||
}
|
||||
}
|
||||
return single_threaded_vector(std::move(parsedCommands));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update recent commands by putting the provided command as most recent.
|
||||
// Upon race condition might override an update made by another window.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CommandPalette::_updateRecentCommands(const hstring& command)
|
||||
{
|
||||
const auto recentCommands = ApplicationState::SharedInstance().RecentCommands();
|
||||
// If this is the first time we've opened the commandline mode and
|
||||
// there aren't any recent commands, then just store the new command.
|
||||
if (!recentCommands)
|
||||
{
|
||||
ApplicationState::SharedInstance().RecentCommands(single_threaded_vector(std::move(std::vector{ command })));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto numNewRecentCommands = std::min(recentCommands.Size() + 1, CommandLineHistoryLength);
|
||||
|
||||
std::vector<hstring> newRecentCommands;
|
||||
newRecentCommands.reserve(numNewRecentCommands);
|
||||
|
||||
std::unordered_set<hstring> uniqueCommands;
|
||||
uniqueCommands.reserve(numNewRecentCommands);
|
||||
|
||||
newRecentCommands.push_back(command);
|
||||
uniqueCommands.insert(command);
|
||||
|
||||
for (const auto& c : recentCommands)
|
||||
{
|
||||
if (newRecentCommands.size() >= CommandLineHistoryLength)
|
||||
{
|
||||
// Don't store more than CommandLineHistoryLength commands
|
||||
break;
|
||||
}
|
||||
|
||||
if (uniqueCommands.emplace(c).second)
|
||||
{
|
||||
newRecentCommands.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationState::SharedInstance().RecentCommands(single_threaded_vector(std::move(newRecentCommands)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,15 +123,16 @@ namespace winrt::TerminalApp::implementation
|
||||
void _dispatchCommand(winrt::TerminalApp::FilteredCommand const& command);
|
||||
void _dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command);
|
||||
void _switchToTab(winrt::TerminalApp::FilteredCommand const& command);
|
||||
std::optional<winrt::TerminalApp::FilteredCommand> _buildCommandLineCommand(std::wstring const& commandLine);
|
||||
static std::optional<winrt::TerminalApp::FilteredCommand> _buildCommandLineCommand(const winrt::hstring& commandLine);
|
||||
|
||||
void _dismissPalette();
|
||||
|
||||
void _scrollToIndex(uint32_t index);
|
||||
uint32_t _getNumVisibleItems();
|
||||
|
||||
static constexpr int CommandLineHistoryLength = 10;
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandLineHistory{ nullptr };
|
||||
static constexpr uint32_t CommandLineHistoryLength = 20;
|
||||
static Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _loadRecentCommands();
|
||||
static void _updateRecentCommands(const winrt::hstring& command);
|
||||
::TerminalApp::AppCommandlineArgs _appArgs;
|
||||
|
||||
void _choosingItemContainer(Windows::UI::Xaml::Controls::ListViewBase const& sender, Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs const& args);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
d:DesignHeight="36"
|
||||
d:DesignHeight="40"
|
||||
d:DesignWidth="400"
|
||||
Background="Transparent"
|
||||
Orientation="Horizontal"
|
||||
@@ -124,8 +124,9 @@
|
||||
tabs will be flush with the top of the window. See GH#2541 for
|
||||
details.
|
||||
-->
|
||||
<x:Double x:Key="CaptionButtonHeightWindowed">36.0</x:Double>
|
||||
<x:Double x:Key="CaptionButtonHeightMaximized">32.0</x:Double>
|
||||
<x:Double x:Key="CaptionButtonHeightWindowed">40.0</x:Double>
|
||||
<!-- 32 + 1 to compensate for GH#10746 -->
|
||||
<x:Double x:Key="CaptionButtonHeightMaximized">33.0</x:Double>
|
||||
|
||||
<Style x:Key="CaptionButton"
|
||||
TargetType="Button">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,11 @@ namespace TerminalAppLocalTests
|
||||
class TabTests;
|
||||
};
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct TerminalTab;
|
||||
}
|
||||
|
||||
enum class Borders : int
|
||||
{
|
||||
None = 0x0,
|
||||
@@ -42,13 +47,21 @@ DEFINE_ENUM_FLAG_OPERATORS(Borders);
|
||||
class Pane : public std::enable_shared_from_this<Pane>
|
||||
{
|
||||
public:
|
||||
Pane(const GUID& profile,
|
||||
Pane(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control,
|
||||
const bool lastFocused = false);
|
||||
|
||||
std::shared_ptr<Pane> GetActivePane();
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl();
|
||||
std::optional<GUID> GetFocusedProfile();
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();
|
||||
|
||||
// Method Description:
|
||||
// - If this is a leaf pane, return its profile.
|
||||
// - If this is a branch/root pane, return nullptr.
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const
|
||||
{
|
||||
return _profile;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::Grid GetRootElement();
|
||||
|
||||
@@ -58,17 +71,21 @@ public:
|
||||
void SetActive();
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
|
||||
const GUID& profile);
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
void Relayout();
|
||||
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
bool NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool MovePane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
const std::vector<uint32_t>& mruPanes);
|
||||
bool SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second);
|
||||
|
||||
std::shared_ptr<Pane> NextPane(const std::shared_ptr<Pane> pane);
|
||||
std::shared_ptr<Pane> PreviousPane(const std::shared_ptr<Pane> pane);
|
||||
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
bool ToggleSplitOrientation();
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
@@ -81,6 +98,10 @@ public:
|
||||
void Shutdown();
|
||||
void Close();
|
||||
|
||||
std::shared_ptr<Pane> AttachPane(std::shared_ptr<Pane> pane,
|
||||
winrt::Microsoft::Terminal::Settings::Model::SplitState splitType);
|
||||
std::shared_ptr<Pane> DetachPane(std::shared_ptr<Pane> pane);
|
||||
|
||||
int GetLeafPaneCount() const noexcept;
|
||||
|
||||
void Maximize(std::shared_ptr<Pane> zoomedPane);
|
||||
@@ -89,20 +110,47 @@ public:
|
||||
std::optional<uint32_t> Id() noexcept;
|
||||
void Id(uint32_t id) noexcept;
|
||||
bool FocusPane(const uint32_t id);
|
||||
bool FocusPane(const std::shared_ptr<Pane> pane);
|
||||
std::shared_ptr<Pane> FindPane(const uint32_t id);
|
||||
|
||||
bool ContainsReadOnly() const;
|
||||
|
||||
// Method Description:
|
||||
// - A helper method for ad-hoc recursion on a pane tree. Walks the pane
|
||||
// tree, calling a predicate on each pane in a depth-first pattern.
|
||||
// - If the predicate returns true, recursion is stopped early.
|
||||
// Arguments:
|
||||
// - f: The function to be applied to each pane.
|
||||
// Return Value:
|
||||
// - true if the predicate returned true on any pane.
|
||||
template<typename F>
|
||||
//requires std::predicate<F, std::shared_ptr<Pane>>
|
||||
bool WalkTree(F f)
|
||||
{
|
||||
if (f(shared_from_this()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
return _firstChild->WalkTree(f) || _secondChild->WalkTree(f);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& states);
|
||||
|
||||
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
|
||||
DECLARE_EVENT(GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
DECLARE_EVENT(LostFocus, _LostFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
DECLARE_EVENT(PaneRaiseBell, _PaneRaiseBellHandlers, winrt::Windows::Foundation::EventHandler<bool>);
|
||||
DECLARE_EVENT(Detached, _PaneDetachedHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
|
||||
private:
|
||||
struct PanePoint;
|
||||
struct FocusNeighborSearch;
|
||||
struct PaneNeighborSearch;
|
||||
struct SnapSizeResult;
|
||||
struct SnapChildrenSizeResult;
|
||||
struct LayoutSizeNode;
|
||||
@@ -122,7 +170,7 @@ private:
|
||||
std::optional<uint32_t> _id;
|
||||
|
||||
bool _lastActive{ false };
|
||||
std::optional<GUID> _profile{ std::nullopt };
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
|
||||
winrt::event_token _connectionStateChangedToken{ 0 };
|
||||
winrt::event_token _firstClosedToken{ 0 };
|
||||
winrt::event_token _secondClosedToken{ 0 };
|
||||
@@ -143,8 +191,7 @@ private:
|
||||
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
std::shared_ptr<Pane> newPane);
|
||||
|
||||
void _CreateRowColDefinitions();
|
||||
void _ApplySplitDefinitions();
|
||||
@@ -155,15 +202,17 @@ private:
|
||||
bool _Resize(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
|
||||
std::shared_ptr<Pane> _FindParentOfPane(const std::shared_ptr<Pane> pane);
|
||||
bool _IsAdjacent(const std::shared_ptr<Pane> first, const PanePoint firstOffset, const std::shared_ptr<Pane> second, const PanePoint secondOffset, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction) const;
|
||||
FocusNeighborSearch _FindNeighborForPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
FocusNeighborSearch searchResult,
|
||||
const bool focusIsSecondSide,
|
||||
const PanePoint offset);
|
||||
FocusNeighborSearch _FindFocusAndNeighbor(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
const PanePoint offset);
|
||||
std::pair<PanePoint, PanePoint> _GetOffsetsForPane(const PanePoint parentOffset) const;
|
||||
bool _IsAdjacent(const PanePoint firstOffset, const PanePoint secondOffset, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction) const;
|
||||
PaneNeighborSearch _FindNeighborForPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
PaneNeighborSearch searchResult,
|
||||
const bool focusIsSecondSide,
|
||||
const PanePoint offset);
|
||||
PaneNeighborSearch _FindPaneAndNeighbor(const std::shared_ptr<Pane> sourcePane,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
const PanePoint offset);
|
||||
|
||||
void _CloseChild(const bool closeFirst);
|
||||
void _CloseChild(const bool closeFirst, const bool isDetaching);
|
||||
winrt::fire_and_forget _CloseChildRoutine(const bool closeFirst);
|
||||
|
||||
void _FocusFirstChild();
|
||||
@@ -179,7 +228,6 @@ private:
|
||||
SnapChildrenSizeResult _CalcSnappedChildrenSizes(const bool widthOrHeight, const float fullSize) const;
|
||||
SnapSizeResult _CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
void _AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& sizeNode) const;
|
||||
|
||||
winrt::Windows::Foundation::Size _GetMinSize() const;
|
||||
LayoutSizeNode _CreateMinSizeTree(const bool widthOrHeight) const;
|
||||
float _ClampSplitPosition(const bool widthOrHeight, const float requestedValue, const float totalSize) const;
|
||||
@@ -228,13 +276,15 @@ private:
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float scaleX;
|
||||
float scaleY;
|
||||
};
|
||||
|
||||
struct FocusNeighborSearch
|
||||
struct PaneNeighborSearch
|
||||
{
|
||||
std::shared_ptr<Pane> focus;
|
||||
std::shared_ptr<Pane> source;
|
||||
std::shared_ptr<Pane> neighbor;
|
||||
PanePoint focusOffset;
|
||||
PanePoint sourceOffset;
|
||||
};
|
||||
|
||||
struct SnapSizeResult
|
||||
@@ -274,5 +324,6 @@ private:
|
||||
void _AssignChildNode(std::unique_ptr<LayoutSizeNode>& nodeField, const LayoutSizeNode* const newNode);
|
||||
};
|
||||
|
||||
friend struct winrt::TerminalApp::implementation::TerminalTab;
|
||||
friend class ::TerminalAppLocalTests::TabTests;
|
||||
};
|
||||
|
||||
@@ -282,6 +282,16 @@
|
||||
<data name="CmdFocusTabTargetArgDesc" xml:space="preserve">
|
||||
<value>Move focus the tab at the given index</value>
|
||||
</data>
|
||||
<data name="CmdMovePaneTabArgDesc" xml:space="preserve">
|
||||
<value>Move focused pane to the tab at the given index</value>
|
||||
</data>
|
||||
<data name="CmdMovePaneDesc" xml:space="preserve">
|
||||
<value>Move focused pane to another tab</value>
|
||||
</data>
|
||||
<data name="CmdMPDesc" xml:space="preserve">
|
||||
<value>An alias for the "move-pane" subcommand.</value>
|
||||
<comment>{Locked="\"move-pane\""}</comment>
|
||||
</data>
|
||||
<data name="CmdSplitPaneSizeArgDesc" xml:space="preserve">
|
||||
<value>Specify the size as a percentage of the parent pane. Valid values are between (0,1), exclusive.</value>
|
||||
</data>
|
||||
@@ -359,14 +369,10 @@
|
||||
<data name="CmdMoveFocusDirectionArgDesc" xml:space="preserve">
|
||||
<value>The direction to move focus in</value>
|
||||
</data>
|
||||
<data name="CmdMovePaneDesc" xml:space="preserve">
|
||||
<data name="CmdSwapPaneDesc" xml:space="preserve">
|
||||
<value>Swap the focused pane with the adjacent pane in the specified direction</value>
|
||||
</data>
|
||||
<data name="CmdMPDesc" xml:space="preserve">
|
||||
<value>An alias for the "move-pane" subcommand.</value>
|
||||
<comment>{Locked="\"move-pane\""}</comment>
|
||||
</data>
|
||||
<data name="CmdMovePaneDirectionArgDesc" xml:space="preserve">
|
||||
<data name="CmdSwapPaneDirectionArgDesc" xml:space="preserve">
|
||||
<value>The direction to move the focused pane in</value>
|
||||
</data>
|
||||
<data name="CmdFocusDesc" xml:space="preserve">
|
||||
@@ -647,6 +653,14 @@
|
||||
<data name="CommandPaletteMenuItem" xml:space="preserve">
|
||||
<value>Command Palette</value>
|
||||
</data>
|
||||
<data name="TrayIconFocusTerminal" xml:space="preserve">
|
||||
<value>Focus Terminal</value>
|
||||
<comment>This is displayed as a label for the context menu item that focuses the terminal.</comment>
|
||||
</data>
|
||||
<data name="TrayIconWindowSubmenu" xml:space="preserve">
|
||||
<value>Windows</value>
|
||||
<comment>This is displayed as a label for the context menu item that holds the submenu of available windows.</comment>
|
||||
</data>
|
||||
<data name="ShellExtension_OpenInTerminalMenuItem_Dev" xml:space="preserve">
|
||||
<value>Open in Windows Terminal (Dev)</value>
|
||||
<comment>{Locked} The dev build will never be seen in multiple languages</comment>
|
||||
@@ -671,4 +685,16 @@
|
||||
<data name="DropPathTabSplit.Text" xml:space="preserve">
|
||||
<value>Split the window and start in given directory</value>
|
||||
</data>
|
||||
<data name="ExportTabText" xml:space="preserve">
|
||||
<value>Export Text</value>
|
||||
</data>
|
||||
<data name="ExportFailure" xml:space="preserve">
|
||||
<value>Failed to export terminal content</value>
|
||||
</data>
|
||||
<data name="ExportSuccess" xml:space="preserve">
|
||||
<value>Successfully exported terminal content</value>
|
||||
</data>
|
||||
<data name="PlainText" xml:space="preserve">
|
||||
<value>Plain Text</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -28,6 +28,9 @@ using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
using namespace winrt::Windows::Storage;
|
||||
using namespace winrt::Windows::Storage::Pickers;
|
||||
using namespace winrt::Windows::Storage::Provider;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
@@ -59,10 +62,10 @@ namespace winrt::TerminalApp::implementation
|
||||
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
|
||||
try
|
||||
{
|
||||
const auto profileGuid{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
|
||||
_CreateNewTabFromSettings(profileGuid, settings, existingConnection);
|
||||
_CreateNewTabWithProfileAndSettings(profile, settings, existingConnection);
|
||||
|
||||
const uint32_t tabCount = _tabs.Size();
|
||||
const bool usedManualProfile = (newTerminalArgs != nullptr) &&
|
||||
@@ -70,7 +73,7 @@ namespace winrt::TerminalApp::implementation
|
||||
newTerminalArgs.Profile().empty());
|
||||
|
||||
// Lookup the name of the color scheme used by this profile.
|
||||
const auto scheme = _settings.GetColorSchemeForProfile(profileGuid);
|
||||
const auto scheme = _settings.GetColorSchemeForProfile(profile);
|
||||
// If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
|
||||
// that as the empty string.
|
||||
const auto schemeName = scheme ? scheme.Name() : L"\0";
|
||||
@@ -82,7 +85,7 @@ namespace winrt::TerminalApp::implementation
|
||||
TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
|
||||
TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
|
||||
TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
|
||||
TraceLoggingGuid(profileGuid, "ProfileGuid", "The GUID of the profile spawned in the new tab"),
|
||||
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
|
||||
TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
|
||||
TraceLoggingFloat64(settings.DefaultSettings().TintOpacity(), "TintOpacity", "Opacity preference from the settings"),
|
||||
TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
|
||||
@@ -95,45 +98,12 @@ namespace winrt::TerminalApp::implementation
|
||||
CATCH_RETURN();
|
||||
|
||||
// Method Description:
|
||||
// - Creates a new tab with the given settings. If the tab bar is not being
|
||||
// currently displayed, it will be shown.
|
||||
// - Sets up state, event handlers, etc on a tab object that was just made.
|
||||
// Arguments:
|
||||
// - profileGuid: ID to use to lookup profile settings for this connection
|
||||
// - settings: the TerminalSettings object to use to create the TerminalControl with.
|
||||
// - existingConnection: optionally receives a connection from the outside world instead of attempting to create one
|
||||
void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, const TerminalSettingsCreateResult& settings, TerminalConnection::ITerminalConnection existingConnection)
|
||||
// - newTabImpl: the uninitialized tab.
|
||||
void TerminalPage::_InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl)
|
||||
{
|
||||
// Initialize the new tab
|
||||
// Create a connection based on the values in our settings object if we weren't given one.
|
||||
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profileGuid, settings.DefaultSettings());
|
||||
|
||||
// If we had an `existingConnection`, then this is an inbound handoff from somewhere else.
|
||||
// We need to tell it about our size information so it can match the dimensions of what
|
||||
// we are about to present.
|
||||
if (existingConnection)
|
||||
{
|
||||
connection.Resize(settings.DefaultSettings().InitialRows(), settings.DefaultSettings().InitialCols());
|
||||
}
|
||||
|
||||
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
|
||||
if (_settings.GlobalSettings().DebugFeaturesEnabled())
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
if (bothAltsPressed)
|
||||
{
|
||||
std::tie(connection, debugConnection) = OpenDebugTapConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
// Give term control a child of the settings so that any overrides go in the child
|
||||
// This way, when we do a settings reload we just update the parent and the overrides remain
|
||||
auto term = _InitControl(settings, connection);
|
||||
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(profileGuid, term);
|
||||
newTabImpl->Initialize();
|
||||
|
||||
// Add the new tab to the list of our tabs.
|
||||
_tabs.Append(*newTabImpl);
|
||||
@@ -146,7 +116,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_UpdateTabIndices();
|
||||
|
||||
// Hookup our event handlers to the new terminal
|
||||
_RegisterTerminalEvents(term, *newTabImpl);
|
||||
_RegisterTabEvents(*newTabImpl);
|
||||
|
||||
// Don't capture a strong ref to the tab. If the tab is removed as this
|
||||
// is called, we don't really care anymore about handling the event.
|
||||
@@ -204,14 +174,26 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
newTabImpl->ExportTabRequested([weakTab, weakThis{ get_weak() }]() {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab)
|
||||
{
|
||||
page->_ExportTab(*tab);
|
||||
}
|
||||
});
|
||||
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
|
||||
// Set this tab's icon to the icon from the user's profile
|
||||
const auto profile = _settings.FindProfile(profileGuid);
|
||||
if (profile != nullptr && !profile.Icon().empty())
|
||||
if (const auto profile{ newTabImpl->GetFocusedProfile() })
|
||||
{
|
||||
newTabImpl->UpdateIcon(profile.Icon());
|
||||
if (!profile.Icon().empty())
|
||||
{
|
||||
newTabImpl->UpdateIcon(profile.Icon());
|
||||
}
|
||||
}
|
||||
|
||||
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
|
||||
@@ -244,19 +226,73 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
if (debugConnection) // this will only be set if global debugging is on and tap is active
|
||||
{
|
||||
auto newControl = _InitControl(settings, debugConnection);
|
||||
_RegisterTerminalEvents(newControl, *newTabImpl);
|
||||
// Split (auto) with the debug tap.
|
||||
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profileGuid, newControl);
|
||||
}
|
||||
|
||||
// This kicks off TabView::SelectionChanged, in response to which
|
||||
// we'll attach the terminal's Xaml control to the Xaml root.
|
||||
_tabView.SelectedItem(tabViewItem);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a new tab using a specified pane as the root.
|
||||
// Arguments:
|
||||
// - pane: The pane to use as the root.
|
||||
void TerminalPage::_CreateNewTabFromPane(std::shared_ptr<Pane> pane)
|
||||
{
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(pane);
|
||||
_InitializeTab(newTabImpl);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a new tab with the given settings. If the tab bar is not being
|
||||
// currently displayed, it will be shown.
|
||||
// Arguments:
|
||||
// - profile: profile settings for this connection
|
||||
// - settings: the TerminalSettings object to use to create the TerminalControl with.
|
||||
// - existingConnection: optionally receives a connection from the outside world instead of attempting to create one
|
||||
void TerminalPage::_CreateNewTabWithProfileAndSettings(const Profile& profile, const TerminalSettingsCreateResult& settings, TerminalConnection::ITerminalConnection existingConnection)
|
||||
{
|
||||
// Initialize the new tab
|
||||
// Create a connection based on the values in our settings object if we weren't given one.
|
||||
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, settings.DefaultSettings());
|
||||
|
||||
// If we had an `existingConnection`, then this is an inbound handoff from somewhere else.
|
||||
// We need to tell it about our size information so it can match the dimensions of what
|
||||
// we are about to present.
|
||||
if (existingConnection)
|
||||
{
|
||||
connection.Resize(settings.DefaultSettings().InitialRows(), settings.DefaultSettings().InitialCols());
|
||||
}
|
||||
|
||||
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
|
||||
if (_settings.GlobalSettings().DebugFeaturesEnabled())
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
if (bothAltsPressed)
|
||||
{
|
||||
std::tie(connection, debugConnection) = OpenDebugTapConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
// Give term control a child of the settings so that any overrides go in the child
|
||||
// This way, when we do a settings reload we just update the parent and the overrides remain
|
||||
auto term = _InitControl(settings, connection);
|
||||
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(profile, term);
|
||||
_RegisterTerminalEvents(term);
|
||||
_InitializeTab(newTabImpl);
|
||||
|
||||
if (debugConnection) // this will only be set if global debugging is on and tap is active
|
||||
{
|
||||
auto newControl = _InitControl(settings, debugConnection);
|
||||
_RegisterTerminalEvents(newControl);
|
||||
// Split (auto) with the debug tap.
|
||||
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profile, newControl);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the icon of the currently focused terminal control, and set its
|
||||
// tab's icon to that icon.
|
||||
@@ -264,19 +300,9 @@ namespace winrt::TerminalApp::implementation
|
||||
// - tab: the Tab to update the title for.
|
||||
void TerminalPage::_UpdateTabIcon(TerminalTab& tab)
|
||||
{
|
||||
const auto lastFocusedProfileOpt = tab.GetFocusedProfile();
|
||||
if (lastFocusedProfileOpt.has_value())
|
||||
if (const auto profile = tab.GetFocusedProfile())
|
||||
{
|
||||
const auto lastFocusedProfile = lastFocusedProfileOpt.value();
|
||||
const auto matchingProfile = _settings.FindProfile(lastFocusedProfile);
|
||||
if (matchingProfile)
|
||||
{
|
||||
tab.UpdateIcon(matchingProfile.Icon());
|
||||
}
|
||||
else
|
||||
{
|
||||
tab.UpdateIcon({});
|
||||
}
|
||||
tab.UpdateIcon(profile.Icon());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,23 +356,18 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: GH#5047 - In the future, we should get the Profile of
|
||||
// the focused pane, and use that to build a new instance of the
|
||||
// settings so we can duplicate this tab/pane.
|
||||
// TODO: GH#5047 - We're duplicating the whole profile, which might
|
||||
// be a dangling reference to old settings.
|
||||
//
|
||||
// Currently, if the profile doesn't exist anymore in our
|
||||
// settings, we'll silently do nothing.
|
||||
//
|
||||
// In the future, it will be preferable to just duplicate the
|
||||
// current control's settings, but we can't do that currently,
|
||||
// because we won't be able to create a new instance of the
|
||||
// connection without keeping an instance of the original Profile
|
||||
// object around.
|
||||
// In the future, it may be preferable to just duplicate the
|
||||
// current control's live settings (which will include changes
|
||||
// made through VT).
|
||||
|
||||
const auto& profileGuid = tab.GetFocusedProfile();
|
||||
if (profileGuid.has_value())
|
||||
if (auto profile = tab.GetFocusedProfile())
|
||||
{
|
||||
const auto settingsCreateResult{ TerminalSettings::CreateWithProfileByID(_settings, profileGuid.value(), *_bindings) };
|
||||
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
|
||||
profile = GetClosestProfileForDuplicationOfProfile(profile);
|
||||
const auto settingsCreateResult{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
|
||||
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
@@ -354,7 +375,7 @@ namespace winrt::TerminalApp::implementation
|
||||
settingsCreateResult.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
_CreateNewTabFromSettings(profileGuid.value(), settingsCreateResult);
|
||||
_CreateNewTabWithProfileAndSettings(profile, settingsCreateResult);
|
||||
|
||||
const auto runtimeTabText{ tab.GetTabText() };
|
||||
if (!runtimeTabText.empty())
|
||||
@@ -383,6 +404,45 @@ namespace winrt::TerminalApp::implementation
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Exports the content of the Terminal Buffer inside the tab
|
||||
// Arguments:
|
||||
// - tab: tab to export
|
||||
winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (const auto control{ tab.GetActiveTerminalControl() })
|
||||
{
|
||||
const FileSavePicker savePicker;
|
||||
savePicker.as<IInitializeWithWindow>()->Initialize(*_hostingHwnd);
|
||||
savePicker.SuggestedStartLocation(PickerLocationId::Downloads);
|
||||
const auto fileChoices = single_threaded_vector<hstring>({ L".txt" });
|
||||
savePicker.FileTypeChoices().Insert(RS_(L"PlainText"), fileChoices);
|
||||
savePicker.SuggestedFileName(control.Title());
|
||||
|
||||
const StorageFile file = co_await savePicker.PickSaveFileAsync();
|
||||
if (file != nullptr)
|
||||
{
|
||||
const auto buffer = control.ReadEntireBuffer();
|
||||
CachedFileManager::DeferUpdates(file);
|
||||
co_await FileIO::WriteTextAsync(file, buffer);
|
||||
const auto status = co_await CachedFileManager::CompleteUpdatesAsync(file);
|
||||
switch (status)
|
||||
{
|
||||
case FileUpdateStatus::Complete:
|
||||
case FileUpdateStatus::CompleteAndRenamed:
|
||||
_ShowControlNoticeDialog(RS_(L"NoticeInfo"), RS_(L"ExportSuccess"));
|
||||
break;
|
||||
default:
|
||||
_ShowControlNoticeDialog(RS_(L"NoticeError"), RS_(L"ExportFailure"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes the tab (both TerminalControl and XAML) after prompting for approval
|
||||
// Arguments:
|
||||
|
||||
@@ -142,6 +142,44 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if (_settings.GlobalSettings().UseAcrylicInTabRow())
|
||||
{
|
||||
const auto res = Application::Current().Resources();
|
||||
|
||||
const auto lightKey = winrt::box_value(L"Light");
|
||||
const auto darkKey = winrt::box_value(L"Dark");
|
||||
const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground");
|
||||
|
||||
for (auto const& dictionary : res.MergedDictionaries())
|
||||
{
|
||||
// Don't change MUX resources
|
||||
if (dictionary.Source())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto const& kvPair : dictionary.ThemeDictionaries())
|
||||
{
|
||||
const auto themeDictionary = kvPair.Value().as<winrt::Windows::UI::Xaml::ResourceDictionary>();
|
||||
|
||||
if (themeDictionary.HasKey(tabViewBackgroundKey))
|
||||
{
|
||||
const auto backgroundSolidBrush = themeDictionary.Lookup(tabViewBackgroundKey).as<Media::SolidColorBrush>();
|
||||
|
||||
const til::color backgroundColor = backgroundSolidBrush.Color();
|
||||
|
||||
const auto acrylicBrush = Media::AcrylicBrush();
|
||||
acrylicBrush.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
|
||||
acrylicBrush.FallbackColor(backgroundColor);
|
||||
acrylicBrush.TintColor(backgroundColor);
|
||||
acrylicBrush.TintOpacity(0.5);
|
||||
|
||||
themeDictionary.Insert(tabViewBackgroundKey, acrylicBrush);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_tabRow.PointerMoved({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
|
||||
_tabView.CanReorderTabs(!isElevated);
|
||||
_tabView.CanDragTabs(!isElevated);
|
||||
@@ -419,6 +457,14 @@ namespace winrt::TerminalApp::implementation
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
// GH#6586: now that we're done processing all startup commands,
|
||||
// focus the active control. This will work as expected for both
|
||||
// commandline invocations and for `wt` action invocations.
|
||||
if (const auto control = _GetActiveControl())
|
||||
{
|
||||
control.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
if (initial)
|
||||
{
|
||||
@@ -733,7 +779,12 @@ namespace winrt::TerminalApp::implementation
|
||||
// Manually fill in the evaluated profile.
|
||||
if (newTerminalArgs.ProfileIndex() != nullptr)
|
||||
{
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(this->_settings.GetProfileForArgs(newTerminalArgs)));
|
||||
// We want to promote the index to a GUID because there is no "launch to profile index" command.
|
||||
const auto profile = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
if (profile)
|
||||
{
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
|
||||
}
|
||||
}
|
||||
this->_OpenNewWindow(false, newTerminalArgs);
|
||||
}
|
||||
@@ -756,15 +807,13 @@ namespace winrt::TerminalApp::implementation
|
||||
// Method Description:
|
||||
// - Creates a new connection based on the profile settings
|
||||
// Arguments:
|
||||
// - the profile GUID we want the settings from
|
||||
// - the profile we want the settings from
|
||||
// - the terminal settings
|
||||
// Return value:
|
||||
// - the desired connection
|
||||
TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(GUID profileGuid,
|
||||
TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(Profile profile,
|
||||
TerminalSettings settings)
|
||||
{
|
||||
const auto profile = _settings.FindProfile(profileGuid);
|
||||
|
||||
TerminalConnection::ITerminalConnection connection{ nullptr };
|
||||
|
||||
winrt::guid connectionType = profile.ConnectionType();
|
||||
@@ -788,7 +837,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
else
|
||||
{
|
||||
std::wstring guidWString = Utils::GuidToString(profileGuid);
|
||||
// profile is guaranteed to exist here
|
||||
std::wstring guidWString = Utils::GuidToString(profile.Guid());
|
||||
|
||||
StringMap envMap{};
|
||||
envMap.Insert(L"WT_PROFILE_ID", guidWString);
|
||||
@@ -837,7 +887,7 @@ namespace winrt::TerminalApp::implementation
|
||||
"ConnectionCreated",
|
||||
TraceLoggingDescription("Event emitted upon the creation of a connection"),
|
||||
TraceLoggingGuid(connectionType, "ConnectionTypeGuid", "The type of the connection"),
|
||||
TraceLoggingGuid(profileGuid, "ProfileGuid", "The profile's GUID"),
|
||||
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The profile's GUID"),
|
||||
TraceLoggingGuid(sessionGuid, "SessionGuid", "The WT_SESSION's GUID"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
@@ -980,11 +1030,9 @@ namespace winrt::TerminalApp::implementation
|
||||
// handle. This includes:
|
||||
// * the Copy and Paste events, for setting and retrieving clipboard data
|
||||
// on the right thread
|
||||
// * the TitleChanged event, for changing the text of the tab
|
||||
// Arguments:
|
||||
// - term: The newly created TermControl to connect the events for
|
||||
// - hostingTab: The Tab that's hosting this TermControl instance
|
||||
void TerminalPage::_RegisterTerminalEvents(TermControl term, TerminalTab& hostingTab)
|
||||
void TerminalPage::_RegisterTerminalEvents(TermControl term)
|
||||
{
|
||||
term.RaiseNotice({ this, &TerminalPage::_ControlNoticeRaisedHandler });
|
||||
|
||||
@@ -999,10 +1047,20 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
term.HidePointerCursor({ get_weak(), &TerminalPage::_HidePointerCursorHandler });
|
||||
term.RestorePointerCursor({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
|
||||
// Add an event handler for when the terminal or tab wants to set a
|
||||
// progress indicator on the taskbar
|
||||
term.SetTaskbarProgress({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
|
||||
}
|
||||
|
||||
// Bind Tab events to the TermControl and the Tab's Pane
|
||||
hostingTab.Initialize(term);
|
||||
|
||||
// Method Description:
|
||||
// - Connects event handlers to the TerminalTab for events that we want to
|
||||
// handle. This includes:
|
||||
// * the TitleChanged event, for changing the text of the tab
|
||||
// * the Color{Selected,Cleared} events to change the color of a tab.
|
||||
// Arguments:
|
||||
// - hostingTab: The Tab that's hosting this TermControl instance
|
||||
void TerminalPage::_RegisterTabEvents(TerminalTab& hostingTab)
|
||||
{
|
||||
auto weakTab{ hostingTab.get_weak() };
|
||||
auto weakThis{ get_weak() };
|
||||
// PropertyChanged is the generic mechanism by which the Tab
|
||||
@@ -1054,7 +1112,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// Add an event handler for when the terminal or tab wants to set a
|
||||
// progress indicator on the taskbar
|
||||
hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
|
||||
term.SetTaskbarProgress({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
|
||||
|
||||
// TODO GH#3327: Once we support colorizing the NewTab button based on
|
||||
// the color of the tab, we'll want to make sure to call
|
||||
@@ -1107,7 +1164,6 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
return terminalTab->NavigateFocus(direction);
|
||||
}
|
||||
return false;
|
||||
@@ -1115,18 +1171,19 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to swap the positions of the focused pane with another pane.
|
||||
// See Pane::MovePane for details.
|
||||
// See Pane::SwapPane for details.
|
||||
// Arguments:
|
||||
// - direction: The direction to move the focused pane in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_MovePane(const FocusDirection& direction)
|
||||
// - true if panes were swapped.
|
||||
bool TerminalPage::_SwapPane(const FocusDirection& direction)
|
||||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->MovePane(direction);
|
||||
return terminalTab->SwapPane(direction);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TermControl TerminalPage::_GetActiveControl()
|
||||
@@ -1188,6 +1245,56 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Moves the currently active pane on the currently active tab to the
|
||||
// specified tab. If the tab index is greater than the number of
|
||||
// tabs, then a new tab will be created for the pane. Similarly, if a pane
|
||||
// is the last remaining pane on a tab, that tab will be closed upon moving.
|
||||
// - No move will occur if the tabIdx is the same as the current tab, or if
|
||||
// the specified tab is not a host of terminals (such as the settings tab).
|
||||
// Arguments:
|
||||
// - tabIdx: The target tab index.
|
||||
// Return Value:
|
||||
// - true if the pane was successfully moved to the new tab.
|
||||
bool TerminalPage::_MovePane(const uint32_t tabIdx)
|
||||
{
|
||||
auto focusedTab{ _GetFocusedTabImpl() };
|
||||
|
||||
if (!focusedTab)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we are trying to move from the current tab to the current tab do nothing.
|
||||
if (_GetFocusedTabIndex() == tabIdx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Moving the pane from the current tab might close it, so get the next
|
||||
// tab before its index changes.
|
||||
if (_tabs.Size() > tabIdx)
|
||||
{
|
||||
auto targetTab = _GetTerminalTabImpl(_tabs.GetAt(tabIdx));
|
||||
// if the selected tab is not a host of terminals (e.g. settings)
|
||||
// don't attempt to add a pane to it.
|
||||
if (!targetTab)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto pane = focusedTab->DetachPane();
|
||||
targetTab->AttachPane(pane);
|
||||
_SetFocusedTab(*targetTab);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pane = focusedTab->DetachPane();
|
||||
_CreateNewTabFromPane(pane);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Split the focused pane either horizontally or vertically, and place the
|
||||
// given TermControl into the newly created pane.
|
||||
@@ -1248,23 +1355,22 @@ namespace winrt::TerminalApp::implementation
|
||||
try
|
||||
{
|
||||
TerminalSettingsCreateResult controlSettings{ nullptr };
|
||||
GUID realGuid;
|
||||
bool profileFound = false;
|
||||
Profile profile{ nullptr };
|
||||
|
||||
if (splitMode == SplitType::Duplicate)
|
||||
{
|
||||
std::optional<GUID> current_guid = tab.GetFocusedProfile();
|
||||
if (current_guid)
|
||||
profile = tab.GetFocusedProfile();
|
||||
if (profile)
|
||||
{
|
||||
profileFound = true;
|
||||
controlSettings = TerminalSettings::CreateWithProfileByID(_settings, current_guid.value(), *_bindings);
|
||||
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
|
||||
profile = GetClosestProfileForDuplicationOfProfile(profile);
|
||||
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
|
||||
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
{
|
||||
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
}
|
||||
realGuid = current_guid.value();
|
||||
}
|
||||
// TODO: GH#5047 - In the future, we should get the Profile of
|
||||
// the focused pane, and use that to build a new instance of the
|
||||
@@ -1279,13 +1385,13 @@ namespace winrt::TerminalApp::implementation
|
||||
// connection without keeping an instance of the original Profile
|
||||
// object around.
|
||||
}
|
||||
if (!profileFound)
|
||||
if (!profile)
|
||||
{
|
||||
realGuid = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
profile = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
|
||||
}
|
||||
|
||||
const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings.DefaultSettings());
|
||||
const auto controlConnection = _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings());
|
||||
|
||||
const float contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
|
||||
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
|
||||
@@ -1306,11 +1412,19 @@ namespace winrt::TerminalApp::implementation
|
||||
auto newControl = _InitControl(controlSettings, controlConnection);
|
||||
|
||||
// Hookup our event handlers to the new terminal
|
||||
_RegisterTerminalEvents(newControl, tab);
|
||||
_RegisterTerminalEvents(newControl);
|
||||
|
||||
_UnZoomIfNeeded();
|
||||
|
||||
tab.SplitPane(realSplitType, splitSize, realGuid, newControl);
|
||||
tab.SplitPane(realSplitType, splitSize, profile, newControl);
|
||||
|
||||
// After GH#6586, the control will no longer focus itself
|
||||
// automatically when it's finished being laid out. Manually focus
|
||||
// the control here instead.
|
||||
if (_startupState == StartupState::Initialized)
|
||||
{
|
||||
_GetActiveControl().Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
@@ -1893,40 +2007,56 @@ namespace winrt::TerminalApp::implementation
|
||||
_HookupKeyBindings(_settings.ActionMap());
|
||||
|
||||
// Refresh UI elements
|
||||
auto profiles = _settings.ActiveProfiles();
|
||||
for (const auto& profile : profiles)
|
||||
|
||||
// Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however,
|
||||
// when we stabilize its guid this will become fully safe.
|
||||
std::unordered_map<winrt::guid, std::pair<Profile, TerminalSettingsCreateResult>> profileGuidSettingsMap;
|
||||
const auto profileDefaults{ _settings.ProfileDefaults() };
|
||||
const auto allProfiles{ _settings.AllProfiles() };
|
||||
|
||||
profileGuidSettingsMap.reserve(allProfiles.Size() + 1);
|
||||
|
||||
// Include the Defaults profile for consideration
|
||||
profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr });
|
||||
for (const auto& newProfile : allProfiles)
|
||||
{
|
||||
const auto profileGuid = profile.Guid();
|
||||
|
||||
try
|
||||
{
|
||||
// This can throw an exception if the profileGuid does
|
||||
// not belong to an actual profile in the list of profiles.
|
||||
auto settings{ TerminalSettings::CreateWithProfileByID(_settings, profileGuid, *_bindings) };
|
||||
|
||||
for (auto tab : _tabs)
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(tab))
|
||||
{
|
||||
terminalTab->UpdateSettings(settings, profileGuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
// Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many
|
||||
// panes may not be using all of their profiles at the same time. Lazy evaluation is king!
|
||||
profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr });
|
||||
}
|
||||
|
||||
// GH#2455: If there are any panes with controls that had been
|
||||
// initialized with a Profile that no longer exists in our list of
|
||||
// profiles, we'll leave it unmodified. The profile doesn't exist
|
||||
// anymore, so we can't possibly update its settings.
|
||||
|
||||
// Update the icon of the tab for the currently focused profile in that tab.
|
||||
// Only do this for TerminalTabs. Other types of tabs won't have multiple panes
|
||||
// and profiles so the Title and Icon will be set once and only once on init.
|
||||
for (auto tab : _tabs)
|
||||
for (const auto& tab : _tabs)
|
||||
{
|
||||
if (auto terminalTab = _GetTerminalTabImpl(tab))
|
||||
if (auto terminalTab{ _GetTerminalTabImpl(tab) })
|
||||
{
|
||||
terminalTab->UpdateSettings();
|
||||
|
||||
// Manually enumerate the panes in each tab; this will let us recycle TerminalSettings
|
||||
// objects but only have to iterate one time.
|
||||
terminalTab->GetRootPane()->WalkTree([&](auto&& pane) {
|
||||
if (const auto profile{ pane->GetProfile() })
|
||||
{
|
||||
const auto found{ profileGuidSettingsMap.find(profile.Guid()) };
|
||||
// GH#2455: If there are any panes with controls that had been
|
||||
// initialized with a Profile that no longer exists in our list of
|
||||
// profiles, we'll leave it unmodified. The profile doesn't exist
|
||||
// anymore, so we can't possibly update its settings.
|
||||
if (found != profileGuidSettingsMap.cend())
|
||||
{
|
||||
auto& pair{ found->second };
|
||||
if (!pair.second)
|
||||
{
|
||||
pair.second = TerminalSettings::CreateWithProfile(_settings, pair.first, *_bindings);
|
||||
}
|
||||
pane->UpdateSettings(pair.second, pair.first);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Update the icon of the tab for the currently focused profile in that tab.
|
||||
// Only do this for TerminalTabs. Other types of tabs won't have multiple panes
|
||||
// and profiles so the Title and Icon will be set once and only once on init.
|
||||
_UpdateTabIcon(*terminalTab);
|
||||
|
||||
// Force the TerminalTab to re-grab its currently active control's title.
|
||||
@@ -2399,13 +2529,28 @@ namespace winrt::TerminalApp::implementation
|
||||
// and wait on it hence the locking mechanism.
|
||||
if (Dispatcher().HasThreadAccess())
|
||||
{
|
||||
// TODO: GH 9458 will give us more context so we can try to choose a better profile.
|
||||
auto hr = _OpenNewTab(nullptr, connection);
|
||||
try
|
||||
{
|
||||
NewTerminalArgs newTerminalArgs{};
|
||||
// TODO GH#10952: When we pass the actual commandline (or originating application), the
|
||||
// settings model can choose the right settings based on command matching, or synthesize
|
||||
// a profile from the registry/link settings (TODO GH#9458).
|
||||
// TODO GH#9458: Get and pass the LNK/EXE filenames.
|
||||
// Passing in a commandline forces GetProfileForArgs to use Base Layer instead of Default Profile;
|
||||
// in the future, it can make a better decision based on the value we pull out of the process handle.
|
||||
// TODO GH#5047: When we hang on to the N.T.A., try not to spawn "default... .exe" :)
|
||||
newTerminalArgs.Commandline(L"default-terminal-invocation-placeholder");
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
|
||||
|
||||
// Request a summon of this window to the foreground
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
_CreateNewTabWithProfileAndSettings(profile, settings, connection);
|
||||
|
||||
return hr;
|
||||
// Request a summon of this window to the foreground
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2413,9 +2558,8 @@ namespace winrt::TerminalApp::implementation
|
||||
HRESULT finalVal = S_OK;
|
||||
|
||||
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() {
|
||||
finalVal = _OpenNewTab(nullptr, connection);
|
||||
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
// Re-running ourselves under the dispatcher will cause us to take the first branch above.
|
||||
finalVal = _OnNewConnection(connection);
|
||||
|
||||
latch.count_down();
|
||||
});
|
||||
@@ -2913,4 +3057,16 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
return WindowName() == QuakeWindowName;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This function stops people from duplicating the base profile, because
|
||||
// it gets ~ ~ weird ~ ~ when they do. Remove when TODO GH#5047 is done.
|
||||
Profile TerminalPage::GetClosestProfileForDuplicationOfProfile(const Profile& profile) const noexcept
|
||||
{
|
||||
if (profile == _settings.ProfileDefaults())
|
||||
{
|
||||
return _settings.FindProfile(_settings.GlobalSettings().DefaultProfile());
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,8 +188,9 @@ namespace winrt::TerminalApp::implementation
|
||||
void _CreateNewTabFlyout();
|
||||
void _OpenNewTabDropdown();
|
||||
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
void _CreateNewTabFromSettings(GUID profileGuid, const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
|
||||
void _CreateNewTabFromPane(std::shared_ptr<Pane> pane);
|
||||
void _CreateNewTabWithProfileAndSettings(const Microsoft::Terminal::Settings::Model::Profile& profile, const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
|
||||
|
||||
winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
|
||||
@@ -219,13 +220,16 @@ namespace winrt::TerminalApp::implementation
|
||||
void _DuplicateTab(const TerminalTab& tab);
|
||||
|
||||
void _SplitTab(TerminalTab& tab);
|
||||
winrt::fire_and_forget _ExportTab(const TerminalTab& tab);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::TabBase tab);
|
||||
void _CloseTabAtIndex(uint32_t index);
|
||||
void _RemoveTab(const winrt::TerminalApp::TabBase& tab);
|
||||
winrt::fire_and_forget _RemoveTabs(const std::vector<winrt::TerminalApp::TabBase> tabs);
|
||||
|
||||
void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term, TerminalTab& hostingTab);
|
||||
void _InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl);
|
||||
void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term);
|
||||
void _RegisterTabEvents(TerminalTab& hostingTab);
|
||||
|
||||
void _DismissTabContextMenus();
|
||||
void _FocusCurrentTab(const bool focusAlways);
|
||||
@@ -236,7 +240,8 @@ namespace winrt::TerminalApp::implementation
|
||||
void _SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference<Microsoft::Terminal::Settings::Model::TabSwitcherMode>& customTabSwitcherMode);
|
||||
bool _SelectTab(uint32_t tabIndex);
|
||||
bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
void _MovePane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _MovePane(const uint32_t tabIdx);
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl();
|
||||
std::optional<uint32_t> _GetFocusedTabIndex() const noexcept;
|
||||
@@ -354,6 +359,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _SetFocusMode(const bool inFocusMode);
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetClosestProfileForDuplicationOfProfile(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile) const noexcept;
|
||||
|
||||
#pragma region ActionHandlers
|
||||
// These are all defined in AppActionHandlers.cpp
|
||||
#define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);
|
||||
|
||||
@@ -25,19 +25,66 @@ namespace winrt
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalTab::TerminalTab(const GUID& profile, const TermControl& control)
|
||||
TerminalTab::TerminalTab(const Profile& profile, const TermControl& control)
|
||||
{
|
||||
_rootPane = std::make_shared<Pane>(profile, control, true);
|
||||
|
||||
_rootPane->Id(_nextPaneId);
|
||||
_activePane = _rootPane;
|
||||
_mruPanes.insert(_mruPanes.begin(), _nextPaneId);
|
||||
++_nextPaneId;
|
||||
|
||||
_rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
|
||||
_Setup();
|
||||
}
|
||||
|
||||
TerminalTab::TerminalTab(std::shared_ptr<Pane> rootPane)
|
||||
{
|
||||
_rootPane = rootPane;
|
||||
_activePane = nullptr;
|
||||
|
||||
auto firstId = _nextPaneId;
|
||||
|
||||
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
|
||||
// update the IDs on each pane
|
||||
if (pane->_IsLeaf())
|
||||
{
|
||||
pane->Id(_nextPaneId);
|
||||
_nextPaneId++;
|
||||
}
|
||||
// Try to find the pane marked active (if it exists)
|
||||
if (pane->_lastActive)
|
||||
{
|
||||
_activePane = pane;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// In case none of the panes were already marked as the focus, just
|
||||
// focus the first one.
|
||||
if (_activePane == nullptr)
|
||||
{
|
||||
_rootPane->FocusPane(firstId);
|
||||
_activePane = _rootPane->GetActivePane();
|
||||
}
|
||||
// Set the active control
|
||||
_mruPanes.insert(_mruPanes.begin(), _activePane->Id().value());
|
||||
|
||||
_Setup();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Shared setup for the constructors. Assumed that _rootPane has been set.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_Setup()
|
||||
{
|
||||
_rootClosedToken = _rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
|
||||
_ClosedHandlers(nullptr, nullptr);
|
||||
});
|
||||
|
||||
_activePane = _rootPane;
|
||||
Content(_rootPane->GetRootElement());
|
||||
|
||||
_MakeTabViewItem();
|
||||
@@ -144,19 +191,31 @@ namespace winrt::TerminalApp::implementation
|
||||
// that was last focused.
|
||||
TermControl TerminalTab::GetActiveTerminalControl() const
|
||||
{
|
||||
return _activePane->GetTerminalControl();
|
||||
if (_activePane)
|
||||
{
|
||||
return _activePane->GetTerminalControl();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called after construction of a Tab object to bind event handlers to its
|
||||
// associated Pane and TermControl object
|
||||
// associated Pane and TermControl objects
|
||||
// Arguments:
|
||||
// - control: reference to the TermControl object to bind event to
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::Initialize(const TermControl& control)
|
||||
void TerminalTab::Initialize()
|
||||
{
|
||||
_BindEventHandlers(control);
|
||||
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
|
||||
// Attach event handlers to each new pane
|
||||
_AttachEventHandlersToPane(pane);
|
||||
if (auto control = pane->GetTerminalControl())
|
||||
{
|
||||
_AttachEventHandlersToControl(pane->Id().value(), control);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -198,35 +257,19 @@ namespace winrt::TerminalApp::implementation
|
||||
// Return Value:
|
||||
// - nullopt if no children of this tab were the last control to be
|
||||
// focused, else the GUID of the profile of the last control to be focused
|
||||
std::optional<GUID> TerminalTab::GetFocusedProfile() const noexcept
|
||||
Profile TerminalTab::GetFocusedProfile() const noexcept
|
||||
{
|
||||
return _activePane->GetFocusedProfile();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called after construction of a Tab object to bind event handlers to its
|
||||
// associated Pane and TermControl object
|
||||
// Arguments:
|
||||
// - control: reference to the TermControl object to bind event to
|
||||
// - Attempts to update the settings that apply to this tab.
|
||||
// - Panes are handled elsewhere, by somebody who can establish broader knowledge
|
||||
// of the settings that apply to all tabs.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_BindEventHandlers(const TermControl& control) noexcept
|
||||
void TerminalTab::UpdateSettings()
|
||||
{
|
||||
_AttachEventHandlersToPane(_rootPane);
|
||||
_AttachEventHandlersToControl(control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempts to update the settings of this tab's tree of panes.
|
||||
// Arguments:
|
||||
// - settings: The new TerminalSettingsCreateResult to apply to any matching controls
|
||||
// - profile: The GUID of the profile these settings should apply to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::UpdateSettings(const TerminalSettingsCreateResult& settings, const GUID& profile)
|
||||
{
|
||||
_rootPane->UpdateSettings(settings, profile);
|
||||
|
||||
// The tabWidthMode may have changed, update the header control accordingly
|
||||
_UpdateHeaderControlMaxWidth();
|
||||
}
|
||||
@@ -405,7 +448,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::SplitPane(SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const Profile& profile,
|
||||
TermControl& control)
|
||||
{
|
||||
// Make sure to take the ID before calling Split() - Split() will clear out the active pane's ID
|
||||
@@ -425,10 +468,10 @@ namespace winrt::TerminalApp::implementation
|
||||
++_nextPaneId;
|
||||
}
|
||||
_activePane = first;
|
||||
_AttachEventHandlersToControl(control);
|
||||
|
||||
// Add a event handlers to the new panes' GotFocus event. When the pane
|
||||
// gains focus, we'll mark it as the new active pane.
|
||||
_AttachEventHandlersToControl(second->Id().value(), control);
|
||||
_AttachEventHandlersToPane(first);
|
||||
_AttachEventHandlersToPane(second);
|
||||
|
||||
@@ -439,11 +482,121 @@ namespace winrt::TerminalApp::implementation
|
||||
_UpdateActivePane(second);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes the currently active pane from this tab. If that was the only
|
||||
// remaining pane, then the entire tab is closed as well.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The removed pane, if the remove succeeded.
|
||||
std::shared_ptr<Pane> TerminalTab::DetachPane()
|
||||
{
|
||||
// if we only have one pane, remove it entirely
|
||||
// and close this tab
|
||||
if (_rootPane == _activePane)
|
||||
{
|
||||
return DetachRoot();
|
||||
}
|
||||
|
||||
// Attempt to remove the active pane from the tree
|
||||
if (const auto pane = _rootPane->DetachPane(_activePane))
|
||||
{
|
||||
// Just make sure that the remaining pane is marked active
|
||||
_UpdateActivePane(_rootPane->GetActivePane());
|
||||
|
||||
return pane;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Closes this tab and returns the root pane to be used elsewhere.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The root pane.
|
||||
std::shared_ptr<Pane> TerminalTab::DetachRoot()
|
||||
{
|
||||
// remove the closed event handler since we are closing the tab
|
||||
// manually.
|
||||
_rootPane->Closed(_rootClosedToken);
|
||||
auto p = _rootPane;
|
||||
p->WalkTree([](auto pane) {
|
||||
pane->_PaneDetachedHandlers(pane);
|
||||
return false;
|
||||
});
|
||||
|
||||
// Clean up references and close the tab
|
||||
_rootPane = nullptr;
|
||||
_activePane = nullptr;
|
||||
Content(nullptr);
|
||||
_ClosedHandlers(nullptr, nullptr);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Add an arbitrary pane to this tab. This will be added as a split on the
|
||||
// currently active pane.
|
||||
// Arguments:
|
||||
// - pane: The pane to add.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::AttachPane(std::shared_ptr<Pane> pane)
|
||||
{
|
||||
// Add the new event handlers to the new pane(s)
|
||||
// and update their ids.
|
||||
pane->WalkTree([&](auto p) {
|
||||
_AttachEventHandlersToPane(p);
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
p->Id(_nextPaneId);
|
||||
_nextPaneId++;
|
||||
}
|
||||
if (auto control = p->GetTerminalControl())
|
||||
{
|
||||
_AttachEventHandlersToControl(p->Id().value(), control);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// pass the old id to the new child
|
||||
const auto previousId = _activePane->Id();
|
||||
|
||||
// Add the new pane as an automatic split on the active pane.
|
||||
auto first = _activePane->AttachPane(pane, SplitState::Automatic);
|
||||
|
||||
// under current assumptions this condition should always be true.
|
||||
if (previousId)
|
||||
{
|
||||
first->Id(previousId.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
first->Id(_nextPaneId);
|
||||
++_nextPaneId;
|
||||
}
|
||||
|
||||
// Update with event handlers on the new child.
|
||||
_activePane = first;
|
||||
_AttachEventHandlersToPane(first);
|
||||
|
||||
// Make sure that we have the right pane set as the active pane
|
||||
pane->WalkTree([&](auto p) {
|
||||
if (p->_lastActive)
|
||||
{
|
||||
_UpdateActivePane(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Find the currently active pane, and then switch the split direction of
|
||||
// its parent. E.g. switch from Horizontal to Vertical.
|
||||
// Return Value:
|
||||
|
||||
// - <none>
|
||||
void TerminalTab::ToggleSplitOrientation()
|
||||
{
|
||||
@@ -495,21 +648,21 @@ namespace winrt::TerminalApp::implementation
|
||||
// to the terminal when no other panes are present (GH#6219)
|
||||
bool TerminalTab::NavigateFocus(const FocusDirection& direction)
|
||||
{
|
||||
if (direction == FocusDirection::Previous)
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
if (const auto newFocus = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
|
||||
{
|
||||
if (_mruPanes.size() < 2)
|
||||
const auto res = _rootPane->FocusPane(newFocus);
|
||||
|
||||
if (_zoomedPane)
|
||||
{
|
||||
return false;
|
||||
UpdateZoom(newFocus);
|
||||
}
|
||||
// To get to the previous pane, get the id of the previous pane and focus to that
|
||||
return _rootPane->FocusPane(_mruPanes.at(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
return _rootPane->NavigateFocus(direction);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -519,26 +672,17 @@ namespace winrt::TerminalApp::implementation
|
||||
// Arguments:
|
||||
// - direction: The direction to move the pane in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::MovePane(const FocusDirection& direction)
|
||||
// - true if two panes were swapped.
|
||||
bool TerminalTab::SwapPane(const FocusDirection& direction)
|
||||
{
|
||||
if (direction == FocusDirection::Previous)
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
if (auto neighbor = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
|
||||
{
|
||||
if (_mruPanes.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (auto lastPane = _rootPane->FindPane(_mruPanes.at(1)))
|
||||
{
|
||||
_rootPane->SwapPanes(_activePane, lastPane);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
_rootPane->MovePane(direction);
|
||||
return _rootPane->SwapPanes(_activePane, neighbor);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TerminalTab::FocusPane(const uint32_t id)
|
||||
@@ -550,7 +694,10 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Prepares this tab for being removed from the UI hierarchy by shutting down all active connections.
|
||||
void TerminalTab::Shutdown()
|
||||
{
|
||||
_rootPane->Shutdown();
|
||||
if (_rootPane)
|
||||
{
|
||||
_rootPane->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -595,6 +742,34 @@ namespace winrt::TerminalApp::implementation
|
||||
_headerControl.BeginRename();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes any event handlers set by the tab on the given pane's control.
|
||||
// The pane's ID is the most stable identifier for a given control, because
|
||||
// the control itself doesn't have a particular ID and its pointer is
|
||||
// unstable since it is moved when panes split.
|
||||
// Arguments:
|
||||
// - paneId: The ID of the pane that contains the given control.
|
||||
// - control: the control to remove events from.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_DetachEventHandlersFromControl(const uint32_t paneId, const TermControl& control)
|
||||
{
|
||||
auto it = _controlEvents.find(paneId);
|
||||
if (it != _controlEvents.end())
|
||||
{
|
||||
auto& events = it->second;
|
||||
|
||||
control.TitleChanged(events.titleToken);
|
||||
control.FontSizeChanged(events.fontToken);
|
||||
control.TabColorChanged(events.colorToken);
|
||||
control.SetTaskbarProgress(events.taskbarToken);
|
||||
control.ReadOnlyChanged(events.readOnlyToken);
|
||||
control.FocusFollowMouseRequested(events.focusToken);
|
||||
|
||||
_controlEvents.erase(paneId);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Register any event handlers that we may need with the given TermControl.
|
||||
// This should be called on each and every TermControl that we add to the tree
|
||||
@@ -602,15 +777,17 @@ namespace winrt::TerminalApp::implementation
|
||||
// * notify us when the control's title changed, so we can update our own
|
||||
// title (if necessary)
|
||||
// Arguments:
|
||||
// - paneId: the ID of the pane that this control belongs to.
|
||||
// - control: the TermControl to add events to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_AttachEventHandlersToControl(const TermControl& control)
|
||||
void TerminalTab::_AttachEventHandlersToControl(const uint32_t paneId, const TermControl& control)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
auto dispatcher = TabViewItem().Dispatcher();
|
||||
ControlEventTokens events{};
|
||||
|
||||
control.TitleChanged([weakThis](auto&&, auto&&) {
|
||||
events.titleToken = control.TitleChanged([weakThis](auto&&, auto&&) {
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
@@ -625,16 +802,16 @@ namespace winrt::TerminalApp::implementation
|
||||
// On the latter event, we tell the root pane to resize itself so that its descendants
|
||||
// (including ourself) can properly snap to character grids. In future, we may also
|
||||
// want to do that on regular font changes.
|
||||
control.FontSizeChanged([this](const int /* fontWidth */,
|
||||
const int /* fontHeight */,
|
||||
const bool isInitialChange) {
|
||||
events.fontToken = control.FontSizeChanged([this](const int /* fontWidth */,
|
||||
const int /* fontHeight */,
|
||||
const bool isInitialChange) {
|
||||
if (isInitialChange)
|
||||
{
|
||||
_rootPane->Relayout();
|
||||
}
|
||||
});
|
||||
|
||||
control.TabColorChanged([weakThis](auto&&, auto&&) {
|
||||
events.colorToken = control.TabColorChanged([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
// The control's tabColor changed, but it is not necessarily the
|
||||
@@ -644,7 +821,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget {
|
||||
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget {
|
||||
co_await winrt::resume_foreground(dispatcher);
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
@@ -653,14 +830,14 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
control.ReadOnlyChanged([weakThis](auto&&, auto&&) {
|
||||
events.readOnlyToken = control.ReadOnlyChanged([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_RecalculateAndApplyReadOnly();
|
||||
}
|
||||
});
|
||||
|
||||
control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
|
||||
events.focusToken = control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
|
||||
if (const auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_focusState != FocusState::Unfocused)
|
||||
@@ -672,6 +849,8 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_controlEvents[paneId] = events;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -689,7 +868,10 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::TerminalApp::TaskbarState TerminalTab::GetCombinedTaskbarState() const
|
||||
{
|
||||
std::vector<winrt::TerminalApp::TaskbarState> states;
|
||||
_rootPane->CollectTaskbarStates(states);
|
||||
if (_rootPane)
|
||||
{
|
||||
_rootPane->CollectTaskbarStates(states);
|
||||
}
|
||||
return states.empty() ? winrt::make<winrt::TerminalApp::implementation::TaskbarState>() :
|
||||
*std::min_element(states.begin(), states.end(), TerminalApp::implementation::TaskbarState::ComparePriority);
|
||||
}
|
||||
@@ -798,7 +980,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto weakThis{ get_weak() };
|
||||
std::weak_ptr<Pane> weakPane{ pane };
|
||||
|
||||
pane->GotFocus([weakThis](std::shared_ptr<Pane> sender) {
|
||||
auto gotFocusToken = pane->GotFocus([weakThis](std::shared_ptr<Pane> sender) {
|
||||
// Do nothing if the Tab's lifetime is expired or pane isn't new.
|
||||
auto tab{ weakThis.get() };
|
||||
|
||||
@@ -818,7 +1000,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
pane->LostFocus([weakThis](std::shared_ptr<Pane> /*sender*/) {
|
||||
auto lostFocusToken = pane->LostFocus([weakThis](std::shared_ptr<Pane> /*sender*/) {
|
||||
// Do nothing if the Tab's lifetime is expired or pane isn't new.
|
||||
auto tab{ weakThis.get() };
|
||||
|
||||
@@ -832,7 +1014,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Add a Closed event handler to the Pane. If the pane closes out from
|
||||
// underneath us, and it's zoomed, we want to be able to make sure to
|
||||
// update our state accordingly to un-zoom that pane. See GH#7252.
|
||||
pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget {
|
||||
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_zoomedPane)
|
||||
@@ -857,7 +1039,7 @@ namespace winrt::TerminalApp::implementation
|
||||
});
|
||||
|
||||
// Add a PaneRaiseBell event handler to the Pane
|
||||
pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
|
||||
auto bellToken = pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (visual)
|
||||
@@ -879,6 +1061,40 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// box the event token so that we can give a reference to it in the
|
||||
// event handler.
|
||||
auto detachedToken = std::make_shared<winrt::event_token>();
|
||||
// Add a Detached event handler to the Pane to clean up tab state
|
||||
// and other event handlers when a pane is removed from this tab.
|
||||
*detachedToken = pane->Detached([weakThis, weakPane, gotFocusToken, lostFocusToken, closedToken, bellToken, detachedToken](std::shared_ptr<Pane> /*sender*/) {
|
||||
// Make sure we do this at most once
|
||||
if (auto pane{ weakPane.lock() })
|
||||
{
|
||||
pane->Detached(*detachedToken);
|
||||
pane->GotFocus(gotFocusToken);
|
||||
pane->LostFocus(lostFocusToken);
|
||||
pane->Closed(closedToken);
|
||||
pane->PaneRaiseBell(bellToken);
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (auto control = pane->GetTerminalControl())
|
||||
{
|
||||
tab->_DetachEventHandlersFromControl(pane->Id().value(), control);
|
||||
}
|
||||
|
||||
for (auto i = tab->_mruPanes.begin(); i != tab->_mruPanes.end(); ++i)
|
||||
{
|
||||
if (*i == pane->Id())
|
||||
{
|
||||
tab->_mruPanes.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -974,6 +1190,23 @@ namespace winrt::TerminalApp::implementation
|
||||
splitTabMenuItem.Icon(splitTabSymbol);
|
||||
}
|
||||
|
||||
Controls::MenuFlyoutItem exportTabMenuItem;
|
||||
{
|
||||
// "Split Tab"
|
||||
Controls::FontIcon exportTabSymbol;
|
||||
exportTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
|
||||
exportTabSymbol.Glyph(L"\xE74E"); // Save
|
||||
|
||||
exportTabMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_ExportTabRequestedHandlers();
|
||||
}
|
||||
});
|
||||
exportTabMenuItem.Text(RS_(L"ExportTabText"));
|
||||
exportTabMenuItem.Icon(exportTabSymbol);
|
||||
}
|
||||
|
||||
// Build the menu
|
||||
Controls::MenuFlyout contextMenuFlyout;
|
||||
Controls::MenuFlyoutSeparator menuSeparator;
|
||||
@@ -981,6 +1214,7 @@ namespace winrt::TerminalApp::implementation
|
||||
contextMenuFlyout.Items().Append(renameTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(duplicateTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(splitTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(exportTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(menuSeparator);
|
||||
|
||||
// GH#5750 - When the context menu is dismissed with ESC, toss the focus
|
||||
@@ -1251,6 +1485,22 @@ namespace winrt::TerminalApp::implementation
|
||||
return _rootPane->PreCalculateCanSplit(_activePane, splitType, splitSize, availableSpace).value_or(false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates the zoomed pane when the focus changes
|
||||
// Arguments:
|
||||
// - newFocus: the new pane to be zoomed
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::UpdateZoom(std::shared_ptr<Pane> newFocus)
|
||||
{
|
||||
// clear the existing content so the old zoomed pane can be added back to the root tree
|
||||
Content(nullptr);
|
||||
_rootPane->Restore(_zoomedPane);
|
||||
_zoomedPane = newFocus;
|
||||
_rootPane->Maximize(_zoomedPane);
|
||||
Content(_zoomedPane->GetRootElement());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Toggle our zoom state.
|
||||
// * If we're not zoomed, then zoom the active pane, making it take the
|
||||
@@ -1355,4 +1605,5 @@ namespace winrt::TerminalApp::implementation
|
||||
DEFINE_EVENT(TerminalTab, TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, ExportTabRequested, _ExportTabRequestedHandlers, winrt::delegate<>);
|
||||
}
|
||||
|
||||
@@ -21,21 +21,26 @@ namespace winrt::TerminalApp::implementation
|
||||
struct TerminalTab : TerminalTabT<TerminalTab, TabBase>
|
||||
{
|
||||
public:
|
||||
TerminalTab(const GUID& profile, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
TerminalTab(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
TerminalTab(std::shared_ptr<Pane> rootPane);
|
||||
|
||||
// Called after construction to perform the necessary setup, which relies on weak_ptr
|
||||
void Initialize(const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void Initialize();
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetActiveTerminalControl() const;
|
||||
std::optional<GUID> GetFocusedProfile() const noexcept;
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile() const noexcept;
|
||||
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
|
||||
|
||||
winrt::fire_and_forget Scroll(const int delta);
|
||||
|
||||
std::shared_ptr<Pane> DetachRoot();
|
||||
std::shared_ptr<Pane> DetachPane();
|
||||
void AttachPane(std::shared_ptr<Pane> pane);
|
||||
|
||||
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
|
||||
void ToggleSplitOrientation();
|
||||
@@ -54,10 +59,10 @@ namespace winrt::TerminalApp::implementation
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
void ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
bool NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
void MovePane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool FocusPane(const uint32_t id);
|
||||
|
||||
void UpdateSettings(const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const GUID& profile);
|
||||
void UpdateSettings();
|
||||
winrt::fire_and_forget UpdateTitle();
|
||||
|
||||
void Shutdown() override;
|
||||
@@ -74,6 +79,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void ResetRuntimeTabColor();
|
||||
void ActivateColorPicker();
|
||||
|
||||
void UpdateZoom(std::shared_ptr<Pane> newFocus);
|
||||
void ToggleZoom();
|
||||
bool IsZoomed();
|
||||
void EnterZoom();
|
||||
@@ -85,6 +91,8 @@ namespace winrt::TerminalApp::implementation
|
||||
std::shared_ptr<Pane> GetActivePane() const;
|
||||
winrt::TerminalApp::TaskbarState GetCombinedTaskbarState() const;
|
||||
|
||||
std::shared_ptr<Pane> GetRootPane() const { return _rootPane; }
|
||||
|
||||
winrt::TerminalApp::TerminalTabStatus TabStatus()
|
||||
{
|
||||
return _tabStatus;
|
||||
@@ -96,12 +104,14 @@ namespace winrt::TerminalApp::implementation
|
||||
DECLARE_EVENT(TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(ExportTabRequested, _ExportTabRequestedHandlers, winrt::delegate<>);
|
||||
TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pane> _rootPane{ nullptr };
|
||||
std::shared_ptr<Pane> _activePane{ nullptr };
|
||||
std::shared_ptr<Pane> _zoomedPane{ nullptr };
|
||||
|
||||
winrt::hstring _lastIconPath{};
|
||||
winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{};
|
||||
std::optional<winrt::Windows::UI::Color> _themeTabColor{};
|
||||
@@ -109,6 +119,19 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::TerminalApp::TabHeaderControl _headerControl{};
|
||||
winrt::TerminalApp::TerminalTabStatus _tabStatus{};
|
||||
|
||||
struct ControlEventTokens
|
||||
{
|
||||
winrt::event_token titleToken;
|
||||
winrt::event_token fontToken;
|
||||
winrt::event_token colorToken;
|
||||
winrt::event_token taskbarToken;
|
||||
winrt::event_token readOnlyToken;
|
||||
winrt::event_token focusToken;
|
||||
};
|
||||
std::unordered_map<uint32_t, ControlEventTokens> _controlEvents;
|
||||
|
||||
winrt::event_token _rootClosedToken{};
|
||||
|
||||
std::vector<uint32_t> _mruPanes;
|
||||
uint32_t _nextPaneId{ 0 };
|
||||
|
||||
@@ -121,6 +144,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
|
||||
|
||||
void _Setup();
|
||||
|
||||
std::optional<Windows::UI::Xaml::DispatcherTimer> _bellIndicatorTimer;
|
||||
void _BellIndicatorTimerTick(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
|
||||
|
||||
@@ -133,9 +158,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _RefreshVisualState();
|
||||
|
||||
void _BindEventHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control) noexcept;
|
||||
|
||||
void _AttachEventHandlersToControl(const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void _DetachEventHandlersFromControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void _AttachEventHandlersToControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void _AttachEventHandlersToPane(std::shared_ptr<Pane> pane);
|
||||
|
||||
void _UpdateActivePane(std::shared_ptr<Pane> pane);
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include <winrt/Microsoft.Terminal.Settings.Editor.h>
|
||||
#include <winrt/Microsoft.Terminal.Settings.Model.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Storage.Provider.h>
|
||||
#include <winrt/Windows.Storage.Pickers.h>
|
||||
|
||||
#include <windows.ui.xaml.media.dxinterop.h>
|
||||
|
||||
|
||||
@@ -513,6 +513,16 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void ConptyConnection::ClearBuffer()
|
||||
{
|
||||
// If we haven't connected yet, then we really don't need to do
|
||||
// anything. The connection should already start clear!
|
||||
if (_isConnected())
|
||||
{
|
||||
THROW_IF_FAILED(ConptyClearPseudoConsole(_hPC.get()));
|
||||
}
|
||||
}
|
||||
|
||||
void ConptyConnection::Close() noexcept
|
||||
try
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
void WriteInput(hstring const& data);
|
||||
void Resize(uint32_t rows, uint32_t columns);
|
||||
void Close() noexcept;
|
||||
void ClearBuffer();
|
||||
|
||||
winrt::guid Guid() const noexcept;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Microsoft.Terminal.TerminalConnection
|
||||
{
|
||||
ConptyConnection();
|
||||
Guid Guid { get; };
|
||||
void ClearBuffer();
|
||||
|
||||
static event NewConnectionHandler NewConnection;
|
||||
static void StartInboundListener();
|
||||
|
||||
@@ -104,6 +104,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
auto pfnTerminalTaskbarProgressChanged = std::bind(&ControlCore::_terminalTaskbarProgressChanged, this);
|
||||
_terminal->TaskbarProgressChangedCallback(pfnTerminalTaskbarProgressChanged);
|
||||
|
||||
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
|
||||
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
|
||||
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
|
||||
// the UIA Engine to the renderer. This prevents us from signaling changes to the cursor or buffer.
|
||||
{
|
||||
// First create the render thread.
|
||||
// Then stash a local pointer to the render thread so we can initialize it and enable it
|
||||
// to paint itself *after* we hand off its ownership to the renderer.
|
||||
// We split up construction and initialization of the render thread object this way
|
||||
// because the renderer and render thread have circular references to each other.
|
||||
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
|
||||
auto* const localPointerToThread = renderThread.get();
|
||||
|
||||
// Now create the renderer and initialize the render thread.
|
||||
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(_terminal.get(), nullptr, 0, std::move(renderThread));
|
||||
|
||||
_renderer->SetRendererEnteredErrorStateCallback([weakThis = get_weak()]() {
|
||||
if (auto strongThis{ weakThis.get() })
|
||||
{
|
||||
strongThis->_RendererEnteredErrorStateHandlers(*strongThis, nullptr);
|
||||
}
|
||||
});
|
||||
|
||||
THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
|
||||
}
|
||||
|
||||
// Get our dispatcher. If we're hosted in-proc with XAML, this will get
|
||||
// us the same dispatcher as TermControl::Dispatcher(). If we're out of
|
||||
// proc, this'll return null. We'll need to instead make a new
|
||||
@@ -196,27 +222,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
// First create the render thread.
|
||||
// Then stash a local pointer to the render thread so we can initialize it and enable it
|
||||
// to paint itself *after* we hand off its ownership to the renderer.
|
||||
// We split up construction and initialization of the render thread object this way
|
||||
// because the renderer and render thread have circular references to each other.
|
||||
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
|
||||
auto* const localPointerToThread = renderThread.get();
|
||||
|
||||
// Now create the renderer and initialize the render thread.
|
||||
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(_terminal.get(), nullptr, 0, std::move(renderThread));
|
||||
::Microsoft::Console::Render::IRenderTarget& renderTarget = *_renderer;
|
||||
|
||||
_renderer->SetRendererEnteredErrorStateCallback([weakThis = get_weak()]() {
|
||||
if (auto strongThis{ weakThis.get() })
|
||||
{
|
||||
strongThis->_RendererEnteredErrorStateHandlers(*strongThis, nullptr);
|
||||
}
|
||||
});
|
||||
|
||||
THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
|
||||
|
||||
// Set up the DX Engine
|
||||
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
_renderer->AddRenderEngine(dxEngine.get());
|
||||
@@ -248,7 +253,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_settings.InitialCols(width);
|
||||
_settings.InitialRows(height);
|
||||
|
||||
_terminal->CreateFromSettings(_settings, renderTarget);
|
||||
_terminal->CreateFromSettings(_settings, *_renderer);
|
||||
|
||||
// IMPORTANT! Set this callback up sooner than later. If we do it
|
||||
// after Enable, then it'll be possible to paint the frame once
|
||||
@@ -264,6 +269,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
|
||||
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
|
||||
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
|
||||
_renderEngine->SetIntenseIsBold(_settings.IntenseIsBold());
|
||||
|
||||
_updateAntiAliasingMode(_renderEngine.get());
|
||||
|
||||
@@ -408,6 +414,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// This is a scroll event that wasn't initiated by the terminal
|
||||
// itself - it was initiated by the mouse wheel, or the scrollbar.
|
||||
_terminal->UserScrollViewport(viewTop);
|
||||
|
||||
_updatePatternLocations->Run();
|
||||
}
|
||||
|
||||
void ControlCore::AdjustOpacity(const double adjustment)
|
||||
@@ -600,6 +608,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
|
||||
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
|
||||
|
||||
_updateAntiAliasingMode(_renderEngine.get());
|
||||
|
||||
// Refresh our font with the renderer
|
||||
@@ -629,6 +638,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_renderEngine->SetSelectionBackground(til::color{ newAppearance.SelectionBackground() });
|
||||
_renderEngine->SetRetroTerminalEffect(newAppearance.RetroTerminalEffect());
|
||||
_renderEngine->SetPixelShaderPath(newAppearance.PixelShaderPath());
|
||||
_renderEngine->SetIntenseIsBold(_settings.IntenseIsBold());
|
||||
_renderer->TriggerRedrawAll();
|
||||
}
|
||||
}
|
||||
@@ -917,6 +927,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
|
||||
bool ControlCore::UpdateSelection(Core::SelectionDirection direction, Core::SelectionExpansion mode)
|
||||
{
|
||||
// - SelectionDirection cannot be none
|
||||
// - A selection must be active for us to update it
|
||||
if (direction == Core::SelectionDirection::None || !HasSelection())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->UpdateSelection(direction, mode);
|
||||
_renderer->TriggerSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called when the Terminal wants to set something to the clipboard, i.e.
|
||||
// when an OSC 52 is emitted.
|
||||
void ControlCore::_terminalCopyToClipboard(std::wstring_view wstr)
|
||||
@@ -1412,18 +1437,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// handle ALT key
|
||||
_terminal->SetBlockSelection(altEnabled);
|
||||
|
||||
::Terminal::SelectionExpansionMode mode = ::Terminal::SelectionExpansionMode::Cell;
|
||||
Core::SelectionExpansion mode = Core::SelectionExpansion::Char;
|
||||
if (numberOfClicks == 1)
|
||||
{
|
||||
mode = ::Terminal::SelectionExpansionMode::Cell;
|
||||
mode = Core::SelectionExpansion::Char;
|
||||
}
|
||||
else if (numberOfClicks == 2)
|
||||
{
|
||||
mode = ::Terminal::SelectionExpansionMode::Word;
|
||||
mode = Core::SelectionExpansion::Word;
|
||||
}
|
||||
else if (numberOfClicks == 3)
|
||||
{
|
||||
mode = ::Terminal::SelectionExpansionMode::Line;
|
||||
mode = Core::SelectionExpansion::Line;
|
||||
}
|
||||
|
||||
// Update the selection appropriately
|
||||
@@ -1448,7 +1473,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_terminal->SetSelectionEnd(terminalPosition, mode);
|
||||
selectionNeedsToBeCopied = true;
|
||||
}
|
||||
else if (mode != ::Terminal::SelectionExpansionMode::Cell || shiftEnabled)
|
||||
else if (mode != Core::SelectionExpansion::Char || shiftEnabled)
|
||||
{
|
||||
// If we are handling a double / triple-click or shift+single click
|
||||
// we establish selection using the selected mode
|
||||
@@ -1462,10 +1487,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine)
|
||||
{
|
||||
if (_renderer)
|
||||
{
|
||||
_renderer->AddRenderEngine(pEngine);
|
||||
}
|
||||
// _renderer will always exist since it's introduced in the ctor
|
||||
_renderer->AddRenderEngine(pEngine);
|
||||
}
|
||||
|
||||
bool ControlCore::IsInReadOnlyMode() const
|
||||
@@ -1491,4 +1514,63 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_updatePatternLocations->Run();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Clear the contents of the buffer. The region cleared is given by
|
||||
// clearType:
|
||||
// * Screen: Clear only the contents of the visible viewport, leaving the
|
||||
// cursor row at the top of the viewport.
|
||||
// * Scrollback: Clear the contents of the scrollback.
|
||||
// * All: Do both - clear the visible viewport and the scrollback, leaving
|
||||
// only the cursor row at the top of the viewport.
|
||||
// Arguments:
|
||||
// - clearType: The type of clear to perform.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ControlCore::ClearBuffer(Control::ClearBufferType clearType)
|
||||
{
|
||||
if (clearType == Control::ClearBufferType::Scrollback || clearType == Control::ClearBufferType::All)
|
||||
{
|
||||
_terminal->EraseInDisplay(::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType::Scrollback);
|
||||
}
|
||||
|
||||
if (clearType == Control::ClearBufferType::Screen || clearType == Control::ClearBufferType::All)
|
||||
{
|
||||
// Send a signal to conpty to clear the buffer.
|
||||
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
|
||||
{
|
||||
// ConPTY will emit sequences to sync up our buffer with its new
|
||||
// contents.
|
||||
conpty.ClearBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hstring ControlCore::ReadEntireBuffer() const
|
||||
{
|
||||
auto terminalLock = _terminal->LockForWriting();
|
||||
|
||||
const auto& textBuffer = _terminal->GetTextBuffer();
|
||||
|
||||
std::wstringstream ss;
|
||||
const auto lastRow = textBuffer.GetLastNonSpaceCharacter().Y;
|
||||
for (auto rowIndex = 0; rowIndex <= lastRow; rowIndex++)
|
||||
{
|
||||
const auto& row = textBuffer.GetRowByOffset(rowIndex);
|
||||
auto rowText = row.GetText();
|
||||
const auto strEnd = rowText.find_last_not_of(UNICODE_SPACE);
|
||||
if (strEnd != std::string::npos)
|
||||
{
|
||||
rowText.erase(strEnd + 1);
|
||||
ss << rowText;
|
||||
}
|
||||
|
||||
if (!row.WasWrapForced())
|
||||
{
|
||||
ss << UNICODE_CARRIAGERETURN << UNICODE_LINEFEED;
|
||||
}
|
||||
}
|
||||
|
||||
return hstring(ss.str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -112,6 +112,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const short wheelDelta,
|
||||
const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state);
|
||||
void UserScrollViewport(const int viewTop);
|
||||
|
||||
void ClearBuffer(Control::ClearBufferType clearType);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
void BlinkAttributeTick();
|
||||
@@ -127,6 +130,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
|
||||
void SetSelectionAnchor(til::point const& position);
|
||||
void SetEndSelectionPoint(til::point const& position);
|
||||
bool UpdateSelection(Core::SelectionDirection direction, Core::SelectionExpansion mode);
|
||||
|
||||
void Search(const winrt::hstring& text,
|
||||
const bool goForward,
|
||||
@@ -144,6 +148,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
bool IsInReadOnlyMode() const;
|
||||
void ToggleReadOnlyMode();
|
||||
|
||||
hstring ReadEntireBuffer() const;
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// clang-format off
|
||||
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
|
||||
|
||||
@@ -22,6 +22,14 @@ namespace Microsoft.Terminal.Control
|
||||
IsRightButtonDown = 0x4
|
||||
};
|
||||
|
||||
|
||||
enum ClearBufferType
|
||||
{
|
||||
Screen,
|
||||
Scrollback,
|
||||
All
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ControlCore : ICoreState
|
||||
{
|
||||
ControlCore(IControlSettings settings,
|
||||
@@ -49,6 +57,7 @@ namespace Microsoft.Terminal.Control
|
||||
Microsoft.Terminal.Core.ControlKeyStates modifiers);
|
||||
void SendInput(String text);
|
||||
void PasteText(String text);
|
||||
void ClearBuffer(ClearBufferType clearType);
|
||||
|
||||
void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
|
||||
void ClearHoveredCell();
|
||||
@@ -71,6 +80,7 @@ namespace Microsoft.Terminal.Control
|
||||
|
||||
Boolean HasSelection { get; };
|
||||
IVector<String> SelectedText(Boolean trimTrailingWhitespace);
|
||||
Boolean UpdateSelection(Microsoft.Terminal.Core.SelectionDirection direction, Microsoft.Terminal.Core.SelectionExpansion mode);
|
||||
|
||||
String HoveredUriText { get; };
|
||||
Windows.Foundation.IReference<Microsoft.Terminal.Core.Point> HoveredCell { get; };
|
||||
@@ -81,6 +91,8 @@ namespace Microsoft.Terminal.Control
|
||||
Boolean CursorOn;
|
||||
void EnablePainting();
|
||||
|
||||
String ReadEntireBuffer();
|
||||
|
||||
event FontSizeChangedEventArgs FontSizeChanged;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, CopyToClipboardEventArgs> CopyToClipboard;
|
||||
|
||||
@@ -614,7 +614,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
Control::InteractivityAutomationPeer ControlInteractivity::OnCreateAutomationPeer()
|
||||
try
|
||||
{
|
||||
auto autoPeer = winrt::make_self<implementation::InteractivityAutomationPeer>(this);
|
||||
const auto autoPeer = winrt::make_self<implementation::InteractivityAutomationPeer>(this);
|
||||
|
||||
_uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get());
|
||||
_core->AttachUiaEngine(_uiaEngine.get());
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Microsoft.Terminal.Control
|
||||
Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode;
|
||||
Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment;
|
||||
Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment;
|
||||
Boolean IntenseIsBold;
|
||||
// IntenseIsBright is in Core Appearance
|
||||
|
||||
// Experimental settings
|
||||
Boolean RetroTerminalEffect;
|
||||
|
||||
@@ -21,9 +21,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
|
||||
KeyChord::KeyChord(bool ctrl, bool alt, bool shift, bool win, int32_t vkey, int32_t scanCode) noexcept :
|
||||
_modifiers{ modifiersFromBooleans(ctrl, alt, shift, win) },
|
||||
_vkey{ vkey },
|
||||
_scanCode{ scanCode }
|
||||
KeyChord(modifiersFromBooleans(ctrl, alt, shift, win), vkey, scanCode)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -32,19 +30,75 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_vkey{ vkey },
|
||||
_scanCode{ scanCode }
|
||||
{
|
||||
// ActionMap needs to identify KeyChords which should "layer" (overwrite) each other.
|
||||
// For instance win+sc(41) and win+` both specify the same KeyChord on an US keyboard layout
|
||||
// from the perspective of a user. Either of the two should correctly overwrite the other.
|
||||
// We can help ActionMap with this by ensuring that Vkey() is always valid.
|
||||
if (!_vkey)
|
||||
{
|
||||
_vkey = MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK_EX);
|
||||
}
|
||||
|
||||
assert(_vkey || _scanCode);
|
||||
}
|
||||
|
||||
VirtualKeyModifiers KeyChord::Modifiers() noexcept
|
||||
uint64_t KeyChord::Hash() const noexcept
|
||||
{
|
||||
// Two KeyChords are equal if they have the same modifiers and either identical
|
||||
// Vkey or ScanCode, with Vkey being preferred. See KeyChord::Equals().
|
||||
// This forces us to _either_ hash _vkey or _scanCode.
|
||||
//
|
||||
// Additionally the hash value with _vkey==123 and _scanCode==123 must be different.
|
||||
// --> Taint hashes of KeyChord without _vkey.
|
||||
auto h = static_cast<uint64_t>(_modifiers) << 32;
|
||||
h |= _vkey ? _vkey : (_scanCode | 0xBABE0000);
|
||||
|
||||
// I didn't like how std::hash uses the byte-wise FNV1a for integers.
|
||||
// So I built my own std::hash with murmurhash3.
|
||||
h ^= h >> 33;
|
||||
h *= UINT64_C(0xff51afd7ed558ccd);
|
||||
h ^= h >> 33;
|
||||
h *= UINT64_C(0xc4ceb9fe1a85ec53);
|
||||
h ^= h >> 33;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
bool KeyChord::Equals(const Control::KeyChord& other) const noexcept
|
||||
{
|
||||
// Two KeyChords are equal if they have the same modifiers and either identical
|
||||
// Vkey or ScanCode, with Vkey being preferred. Vkey is preferred because:
|
||||
// ActionMap needs to identify KeyChords which should "layer" (overwrite) each other.
|
||||
// For instance win+sc(41) and win+` both specify the same KeyChord on an US keyboard layout
|
||||
// from the perspective of a user. Either of the two should correctly overwrite the other.
|
||||
//
|
||||
// Two problems exist here:
|
||||
// * Since a value of 0 indicates that the Vkey/ScanCode isn't set, we cannot use == to compare them.
|
||||
// Otherwise we return true, even if the Vkey/ScanCode isn't set on both sides.
|
||||
// * Whenever Equals() returns true, the Hash() value _must_ be identical.
|
||||
// For instance the code below ensures the preference of Vkey over ScanCode by:
|
||||
// this->_vkey || other->_vkey ? ...vkey... : ...scanCode...
|
||||
// We cannot use "&&", even if it would be technically more correct, as this would mean the
|
||||
// return value of this function would be dependent on the existence of a Vkey in "other".
|
||||
// But Hash() has no "other" argument to consider when deciding if its Vkey or ScanCode should be hashed.
|
||||
//
|
||||
// Bitwise operators are used because MSVC doesn't support compiling
|
||||
// boolean operators into bitwise ones at the time of writing.
|
||||
const auto otherSelf = winrt::get_self<KeyChord>(other);
|
||||
return _modifiers == otherSelf->_modifiers && ((_vkey | otherSelf->_vkey) ? _vkey == otherSelf->_vkey : _scanCode == otherSelf->_scanCode);
|
||||
}
|
||||
|
||||
VirtualKeyModifiers KeyChord::Modifiers() const noexcept
|
||||
{
|
||||
return _modifiers;
|
||||
}
|
||||
|
||||
void KeyChord::Modifiers(VirtualKeyModifiers const& value) noexcept
|
||||
void KeyChord::Modifiers(const VirtualKeyModifiers value) noexcept
|
||||
{
|
||||
_modifiers = value;
|
||||
}
|
||||
|
||||
int32_t KeyChord::Vkey() noexcept
|
||||
int32_t KeyChord::Vkey() const noexcept
|
||||
{
|
||||
return _vkey;
|
||||
}
|
||||
@@ -54,7 +108,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_vkey = value;
|
||||
}
|
||||
|
||||
int32_t KeyChord::ScanCode() noexcept
|
||||
int32_t KeyChord::ScanCode() const noexcept
|
||||
{
|
||||
return _scanCode;
|
||||
}
|
||||
|
||||
@@ -13,11 +13,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
KeyChord(const winrt::Windows::System::VirtualKeyModifiers modifiers, int32_t vkey, int32_t scanCode) noexcept;
|
||||
KeyChord(bool ctrl, bool alt, bool shift, bool win, int32_t vkey, int32_t scanCode) noexcept;
|
||||
|
||||
winrt::Windows::System::VirtualKeyModifiers Modifiers() noexcept;
|
||||
void Modifiers(winrt::Windows::System::VirtualKeyModifiers const& value) noexcept;
|
||||
int32_t Vkey() noexcept;
|
||||
uint64_t Hash() const noexcept;
|
||||
bool Equals(const Control::KeyChord& other) const noexcept;
|
||||
|
||||
winrt::Windows::System::VirtualKeyModifiers Modifiers() const noexcept;
|
||||
void Modifiers(const winrt::Windows::System::VirtualKeyModifiers value) noexcept;
|
||||
int32_t Vkey() const noexcept;
|
||||
void Vkey(int32_t value) noexcept;
|
||||
int32_t ScanCode() noexcept;
|
||||
int32_t ScanCode() const noexcept;
|
||||
void ScanCode(int32_t value) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
@@ -10,6 +10,9 @@ namespace Microsoft.Terminal.Control
|
||||
KeyChord(Windows.System.VirtualKeyModifiers modifiers, Int32 vkey, Int32 scanCode);
|
||||
KeyChord(Boolean ctrl, Boolean alt, Boolean shift, Boolean win, Int32 vkey, Int32 scanCode);
|
||||
|
||||
UInt64 Hash();
|
||||
Boolean Equals(KeyChord other);
|
||||
|
||||
Windows.System.VirtualKeyModifiers Modifiers;
|
||||
Int32 Vkey;
|
||||
Int32 ScanCode;
|
||||
|
||||
@@ -351,6 +351,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_core.SendInput(wstr);
|
||||
}
|
||||
void TermControl::ClearBuffer(Control::ClearBufferType clearType)
|
||||
{
|
||||
_core.ClearBuffer(clearType);
|
||||
}
|
||||
|
||||
void TermControl::ToggleShaderEffects()
|
||||
{
|
||||
@@ -377,7 +381,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_changeBackgroundColor(bg);
|
||||
|
||||
// Apply padding as swapChainPanel's margin
|
||||
auto newMargin = ParseThicknessFromPadding(newSettings.Padding());
|
||||
const auto newMargin = ParseThicknessFromPadding(newSettings.Padding());
|
||||
SwapChainPanel().Margin(newMargin);
|
||||
|
||||
TSFInputControl().Margin(newMargin);
|
||||
@@ -511,18 +515,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - The automation peer for our control
|
||||
Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer()
|
||||
{
|
||||
if (_initializedTerminal && !_IsClosing()) // only set up the automation peer if we're ready to go live
|
||||
// MSFT 33353327: We're purposefully not using _initializedTerminal to ensure we're fully initialized.
|
||||
// Doing so makes us return nullptr when XAML requests an automation peer.
|
||||
// Instead, we need to give XAML an automation peer, then fix it later.
|
||||
if (!_IsClosing())
|
||||
{
|
||||
// create a custom automation peer with this code pattern:
|
||||
// (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
|
||||
if (const auto& interactivityAutoPeer{ _interactivity.OnCreateAutomationPeer() })
|
||||
{
|
||||
auto margins{ SwapChainPanel().Margin() };
|
||||
|
||||
Core::Padding padding{ margins.Left,
|
||||
margins.Top,
|
||||
margins.Right,
|
||||
margins.Bottom };
|
||||
const auto margins{ SwapChainPanel().Margin() };
|
||||
const Core::Padding padding{ margins.Left,
|
||||
margins.Top,
|
||||
margins.Right,
|
||||
margins.Bottom };
|
||||
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, padding, interactivityAutoPeer);
|
||||
return _automationPeer;
|
||||
}
|
||||
@@ -679,8 +685,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
DispatcherTimer cursorTimer;
|
||||
cursorTimer.Interval(std::chrono::milliseconds(blinkTime));
|
||||
cursorTimer.Tick({ get_weak(), &TermControl::_CursorTimerTick });
|
||||
cursorTimer.Start();
|
||||
_cursorTimer.emplace(std::move(cursorTimer));
|
||||
// As of GH#6586, don't start the cursor timer immediately, and
|
||||
// don't show the cursor initially. We'll show the cursor and start
|
||||
// the timer when the control is first focused. cursorTimer.Start();
|
||||
_core.CursorOn(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -709,14 +718,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Now that the renderer is set up, update the appearance for initialization
|
||||
_UpdateAppearanceFromUIThread(_settings);
|
||||
|
||||
// Focus the control here. If we do it during control initialization, then
|
||||
// focus won't actually get passed to us. I believe this is because
|
||||
// we're not technically a part of the UI tree yet, so focusing us
|
||||
// becomes a no-op.
|
||||
this->Focus(FocusState::Programmatic);
|
||||
|
||||
_initializedTerminal = true;
|
||||
|
||||
// MSFT 33353327: If the AutomationPeer was created before we were done initializing,
|
||||
// make sure it's properly set up now.
|
||||
if (_automationPeer)
|
||||
{
|
||||
_automationPeer.UpdateControlBounds();
|
||||
const auto margins{ GetPadding() };
|
||||
_automationPeer.SetControlPadding(Core::Padding{ margins.Left,
|
||||
margins.Top,
|
||||
margins.Right,
|
||||
margins.Bottom });
|
||||
}
|
||||
|
||||
// Likewise, run the event handlers outside of lock (they could
|
||||
// be reentrant)
|
||||
_InitializedHandlers(*this, nullptr);
|
||||
@@ -889,6 +904,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
if (vkey == VK_SPACE && modifiers.IsAltPressed())
|
||||
{
|
||||
if (const auto bindings = _settings.KeyBindings())
|
||||
{
|
||||
if (!bindings.IsKeyChordExplicitlyUnbound({ modifiers.IsCtrlPressed(), modifiers.IsAltPressed(), modifiers.IsShiftPressed(), modifiers.IsWinPressed(), vkey, scanCode }))
|
||||
{
|
||||
// If we get here, it means that
|
||||
// 1. we do not have a command bound to alt+space
|
||||
// 2. alt+space was not explicitly unbound
|
||||
// That means that XAML handled the alt+space to open up the context menu, and
|
||||
// so we don't want to send anything to the terminal
|
||||
// TODO GH#11018: Add a new "openSystemMenu" keybinding
|
||||
e.Handled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_TrySendKeyEvent(vkey, scanCode, modifiers, keyDown))
|
||||
{
|
||||
e.Handled(true);
|
||||
@@ -1209,6 +1242,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_RestorePointerCursorHandlers(*this, nullptr);
|
||||
|
||||
const auto point = args.GetCurrentPoint(*this);
|
||||
// GH#10329 - we don't need to handle horizontal scrolls. Only vertical ones.
|
||||
// So filter out the horizontal ones.
|
||||
if (point.Properties().IsHorizontalMouseWheel())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = _interactivity.MouseWheel(ControlKeyStates{ args.KeyModifiers() },
|
||||
point.Properties().MouseWheelDelta(),
|
||||
@@ -2551,4 +2590,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_playWarningBell->Run();
|
||||
}
|
||||
|
||||
hstring TermControl::ReadEntireBuffer() const
|
||||
{
|
||||
return _core.ReadEntireBuffer();
|
||||
}
|
||||
|
||||
bool TermControl::UpdateSelection(Core::SelectionDirection direction, Core::SelectionExpansion mode)
|
||||
{
|
||||
return _core.UpdateSelection(direction, mode);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user