Compare commits

..

5 Commits

Author SHA1 Message Date
Mike Griese
068a98c40e Some percent 0-100 of this is garbage
I'm not sure any of this is right. It all feels wrong. The problem we're
  having is that when conhost defers the EOL wrap, our cursor is still at the
  last cell of a row, and we still try and paint it there. That paintCursor,
  right before the end of the frame, is what's screwing us up

  I'm pretty sure the RenderData code to move the cursor ++ when we're at a
  delayed EOL is definitely wrong
2020-03-25 17:06:13 -05:00
Mike Griese
c0bb37c291 This looks like it might hypothetically work, lets check the tests... 2020-03-25 15:13:50 -05:00
Mike Griese
9d79c5c3fe Revert "This is complicated scrolling math and I hate it"
This reverts commit 112cb0ab8d.
2020-03-25 14:43:54 -05:00
Mike Griese
112cb0ab8d This is complicated scrolling math and I hate it 2020-03-25 14:43:34 -05:00
Mike Griese
d972b5e07a More tracing is always good 2020-03-25 14:41:36 -05:00
150 changed files with 1263 additions and 7125 deletions

View File

@@ -1,15 +0,0 @@
# 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.

View File

@@ -1,48 +0,0 @@
<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details>
<summary>
:pencil2: Contributor please read this
</summary>
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
:warning: The command is written for posix shells. If it doesn't work for you, you can manually _add_ (one word per line) / _remove_ items to `expect.txt` and the `excludes.txt` files.
If the listed items are:
* ... **misspelled**, then please *correct* them instead of using the command.
* ... *names*, please add them to `.github/actions/spelling/allow/names.txt`.
* ... APIs, you can add them to a file in `.github/actions/spelling/allow/`.
* ... just things you're using, please add them to an appropriate file in `.github/actions/spelling/expect/`.
* ... tokens you only need in one place and shouldn't *generally be used*, you can add an item in an appropriate file in `.github/actions/spelling/patterns/`.
See the `README.md` in each directory for more information.
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
<details><summary>If the flagged items are :exploding_head: false positives</summary>
If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).
Please add a file path to the `excludes.txt` file matching the containing file.
File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).
* well-formed pattern.
If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
try adding it to the `patterns.txt` file.
Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
Note that patterns can't match multiline strings.
</details>
</details>

View File

@@ -1,21 +0,0 @@
# Allow files are lists of words to accept unconditionally
While check spelling will complain about an expected word
which is no longer present, you can include things here even if
they are not otherwise present in the repository.
E.g., you could include a list of system APIs here, or potential
contributors (so that if a future commit includes their name,
it'll be accepted).
## Files
| File | Description |
| ---- | ----------- |
| [Allow](allow.txt) | Supplements to the dictionary |
| [Chinese](chinese.txt) | Chinese words |
| [Japanese](japanese.txt) | Japanese words |
| [Microsoft](microsoft.txt) | Microsoft brand items |
| [Fonts](fonts.txt) | Font names |
| [Names](names.txt) | Names of people |
| [Colors](colors.txt) | Names of color |

View File

@@ -1,108 +0,0 @@
admins
allcolors
Apc
apc
breadcrumb
breadcrumbs
bsd
calt
ccmp
changelog
clickable
clig
CMMI
copyable
cybersecurity
dalet
Dcs
dcs
dialytika
dje
downside
downsides
dze
dzhe
EDDB
EDDC
Enum'd
Fitt
formattings
FTCS
ftp
fvar
gantt
gcc
geeksforgeeks
ghe
github
gje
godbolt
hostname
hostnames
https
hyperlink
hyperlinking
hyperlinks
iconify
img
inlined
It'd
kje
libfuzzer
libuv
liga
lje
Llast
llvm
Lmid
locl
lol
lorem
Lorigin
maxed
minimalistic
mkmk
mnt
mru
nje
noreply
ogonek
ok'd
overlined
pipeline
postmodern
ptys
qof
qps
rclt
reimplementation
reserialization
reserialize
reserializes
rlig
runtimes
shcha
slnt
Sos
ssh
timeline
timelines
timestamped
TLDR
tokenizes
tonos
toolset
tshe
ubuntu
uiatextrange
UIs
und
unregister
versioned
vsdevcmd
We'd
wildcards
XBox
YBox
yeru
zhe

View File

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

View File

@@ -1,5 +0,0 @@
CHINESEBIG
choseong
Jongseong
Jungseong
ssangtikeut

View File

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

View File

@@ -1,10 +0,0 @@
Consolas
emoji
emojis
Extralight
Gabriola
Iosevka
MDL
Monofur
Segoe
wght

View File

@@ -1,4 +0,0 @@
arigatoo
doomo
Kaomojis
TATEGAKI

View File

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

View File

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

View File

@@ -1,91 +0,0 @@
Anup
austdi
arkthur
Ballmer
bhoj
Bhojwani
Bluloco
carlos
dhowett
Diviness
dsafa
duhowett
DXP
ekg
eryksun
ethanschoonover
Firefox
Gatta
glsl
Gravell
Grie
Griese
Hernan
Howett
Illhardt
iquilezles
italo
jantari
jerrysh
Kaiyu
kimwalisch
KMehrain
KODELIFE
Kodelife
Kourosh
kowalczyk
leonmsft
Lepilleur
lhecker
lukesampson
Macbook
Manandhar
masserano
mbadolato
Mehrain
menger
mgravell
michaelniksa
michkap
migrie
mikegr
mikemaccana
miloush
miniksa
niksa
nvaccess
nvda
oising
oldnewthing
opengl
osgwiki
pabhojwa
panos
paulcam
pauldotknopf
PGP
Pham
Rincewind
rprichard
Schoonover
shadertoy
Shomnipotence
simioni
Somuah
sonph
sonpham
stakx
talo
thereses
Walisch
WDX
Wellons
Wirt
Wojciech
zadjii
Zamor
Zamora
zamora
Zoey
zorio
Zverovich

View File

@@ -1,523 +0,0 @@
# marker to ignore all code on line
^.*/\* #no-spell-check-line \*/.*$
# marker for ignoring a comment to the end of the line
// #no-spell-check.*$
# patch hunk comments
^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .*
# git index header
index [0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
# cid urls
(['"])cid:.*?\g{-1}
# data url in parens
\(data:[^)]*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\)
# data url in quotes
([`'"])data:.*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
# data url
data:[-a-zA-Z=;:/0-9+]*,\S*
# mailto urls
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
# magnet urls
magnet:[?=:\w]+
# magnet urls
"magnet:[^"]+"
# obs:
"obs:[^"]*"
# The `\b` here means a break, it's the fancy way to handle urls, but it makes things harder to read
# In this examples content, I'm using a number of different ways to match things to show various approaches
# asciinema
\basciinema\.org/a/[0-9a-zA-Z]+
# apple
\bdeveloper\.apple\.com/[-\w?=/]+
# Apple music
\bembed\.music\.apple\.com/fr/playlist/usr-share/[-\w.]+
# appveyor api
\bci\.appveyor\.com/api/projects/status/[0-9a-z]+
# appveyor project
\bci\.appveyor\.com/project/(?:[^/\s"]*/){2}builds?/\d+/job/[0-9a-z]+
# Amazon
# Amazon
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
# AWS S3
\b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]*
# AWS execute-api
\b[0-9a-z]{10}\.execute-api\.[-0-9a-z]+\.amazonaws\.com\b
# AWS ELB
\b\w+\.[-0-9a-z]+\.elb\.amazonaws\.com\b
# AWS SNS
\bsns\.[-0-9a-z]+.amazonaws\.com/[-\w/&#%_?:=]*
# AWS VPC
vpc-\w+
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
# YouTube url
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
# YouTube music
\bmusic\.youtube\.com/youtubei/v1/browse(?:[?&]\w+=[-a-zA-Z0-9?&=_]*)
# YouTube tag
<\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"]
# YouTube image
\bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]*
# Google Accounts
\baccounts.google.com/[-_/?=.:;+%&0-9a-zA-Z]*
# Google Analytics
\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
# Google APIs
\bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+
# Google Storage
\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
# Google Calendar
\bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+
\w+\@group\.calendar\.google\.com\b
# Google DataStudio
\bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|)
# The leading `/` here is as opposed to the `\b` above
# ... a short way to match `https://` or `http://` since most urls have one of those prefixes
# Google Docs
/docs\.google\.com/[a-z]+/(?:ccc\?key=\w+|(?:u/\d+|d/(?:e/|)[0-9a-zA-Z_-]+/)?(?:edit\?[-\w=#.]*|/\?[\w=&]*|))
# Google Drive
\bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]*
# Google Groups
\bgroups\.google\.com/(?:(?:forum/#!|d/)(?:msg|topics?|searchin)|a)/[^/\s"]+/[-a-zA-Z0-9$]+(?:/[-a-zA-Z0-9]+)*
# Google Maps
\bmaps\.google\.com/maps\?[\w&;=]*
# Google themes
themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
# Google CDN
\bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]*
# Goo.gl
/goo\.gl/[a-zA-Z0-9]+
# Google Chrome Store
\bchrome\.google\.com/webstore/detail/[-\w]*(?:/\w*|)
# Google Books
\bgoogle\.(?:\w{2,4})/books(?:/\w+)*\?[-\w\d=&#.]*
# Google Fonts
\bfonts\.(?:googleapis|gstatic)\.com/[-/?=:;+&0-9a-zA-Z]*
# Google Forms
\bforms\.gle/\w+
# Google Scholar
\bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+
# Google Colab Research Drive
\bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]*
# GitHub SHAs (api)
\bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b
# GitHub SHAs (markdown)
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
# GitHub SHAs
\bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b
# GitHub wiki
\bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b
# githubusercontent
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
# githubassets
\bgithubassets.com/[0-9a-f]+(?:[-/\w.]+)
# gist github
\bgist\.github\.com/[^/\s"]+/[0-9a-f]+
# git.io
\bgit\.io/[0-9a-zA-Z]+
# GitHub JSON
"node_id": "[-a-zA-Z=;:/0-9+]*"
# Contributor
\[[^\]]+\]\(https://github\.com/[^/\s"]+\)
# GHSA
GHSA(?:-[0-9a-z]{4}){3}
# GitLab commit
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
# GitLab merge requests
\bgitlab\.[^/\s"]*/\S+/\S+/-/merge_requests/\d+/diffs#[0-9a-f]{40}\b
# GitLab uploads
\bgitlab\.[^/\s"]*/uploads/[-a-zA-Z=;:/0-9+]*
# GitLab commits
\bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b
# binanace
accounts.binance.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
# bitbucket diff
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+
# bitbucket repositories commits
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
# bitbucket commits
\bbitbucket\.org/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
# bit.ly
\bbit\.ly/\w+
# bitrise
\bapp\.bitrise\.io/app/[0-9a-f]*/[\w.?=&]*
# bootstrapcdn.com
\bbootstrapcdn\.com/[-./\w]+
# cdn.cloudflare.com
\bcdnjs\.cloudflare\.com/[./\w]+
# circleci
\bcircleci\.com/gh(?:/[^/\s"]+){1,5}.[a-z]+\?[-0-9a-zA-Z=&]+
# gitter
\bgitter\.im(?:/[^/\s"]+){2}\?at=[0-9a-f]+
# gravatar
\bgravatar\.com/avatar/[0-9a-f]+
# ibm
[a-z.]*ibm\.com/[-_#=:%!?~.\\/\d\w]*
# imgur
\bimgur\.com/[^.]+
# Internet Archive
\barchive\.org/web/\d+/(?:[-\w.?,'/\\+&%$#_:]*)
# discord
/discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,}
# Disqus
\bdisqus\.com/[-\w/%.()!?&=_]*
# medium link
\blink\.medium\.com/[a-zA-Z0-9]+
# medium
\bmedium\.com/\@?[^/\s"]+/[-\w]+
# microsoft
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
# powerbi
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
# vs devops
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
# microsoft store
\bmicrosoft\.com/store/apps/\w+
# mvnrepository.com
\bmvnrepository\.com/[-0-9a-z./]+
# now.sh
/[0-9a-z-.]+\.now\.sh\b
# oracle
\bdocs\.oracle\.com/[-0-9a-zA-Z./_?#&=]*
# chromatic.com
/\S+.chromatic.com\S*[")]
# codacy
\bapi\.codacy\.com/project/badge/Grade/[0-9a-f]+
# compai
\bcompai\.pub/v1/png/[0-9a-f]+
# mailgun api
\.api\.mailgun\.net/v3/domains/[0-9a-z]+\.mailgun.org/messages/[0-9a-zA-Z=@]*
# mailgun
\b[0-9a-z]+.mailgun.org
# /message-id/
/message-id/[-\w@./%]+
# Reddit
\breddit\.com/r/[/\w_]*
# requestb.in
\brequestb\.in/[0-9a-z]+
# sched
\b[a-z0-9]+\.sched\.com\b
# Slack url
slack://[a-zA-Z0-9?&=]+
# Slack
\bslack\.com/[-0-9a-zA-Z/_~?&=.]*
# Slack edge
\bslack-edge\.com/[-a-zA-Z0-9?&=%./]+
# Slack images
\bslack-imgs\.com/[-a-zA-Z0-9?&=%.]+
# shields.io
\bshields\.io/[-\w/%?=&.:+;,]*
# stackexchange -- https://stackexchange.com/feeds/sites
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
# Sentry
[0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b
# Twitter markdown
\[\@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\)
# Twitter hashtag
\btwitter\.com/hashtag/[\w?_=&]*
# Twitter status
\btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)
# Twitter profile images
\btwimg\.com/profile_images/[_\w./]*
# Twitter media
\btwimg\.com/media/[-_\w./?=]*
# Twitter link shortened
\bt\.co/\w+
# facebook
\bfburl\.com/[0-9a-z_]+
# facebook CDN
\bfbcdn\.net/[\w/.,]*
# facebook watch
\bfb\.watch/[0-9A-Za-z]+
# dropbox
\bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+
# ipfs protocol
ipfs://[0-9a-z]*
# ipfs url
/ipfs/[0-9a-z]*
# w3
\bw3\.org/[-0-9a-zA-Z/#.]+
# loom
\bloom\.com/embed/[0-9a-f]+
# regex101
\bregex101\.com/r/[^/\s"]+/\d+
# figma
\bfigma\.com/file(?:/[0-9a-zA-Z]+/)+
# freecodecamp.org
\bfreecodecamp\.org/[-\w/.]+
# image.tmdb.org
\bimage\.tmdb\.org/[/\w.]+
# mermaid
\bmermaid\.ink/img/[-\w]+|\bmermaid-js\.github\.io/mermaid-live-editor/#/edit/[-\w]+
# Wikipedia
\ben\.wikipedia\.org/wiki/[-\w%.#]+
# gitweb
[^"\s]+/gitweb/\S+;h=[0-9a-f]+
# HyperKitty lists
/archives/list/[^@/]+\@[^/\s"]*/message/[^/\s"]*/
# lists
/thread\.html/[^"\s]+
# list-management
\blist-manage\.com/subscribe(?:[?&](?:u|id)=[0-9a-f]+)+
# kubectl.kubernetes.io/last-applied-configuration
"kubectl.kubernetes.io/last-applied-configuration": ".*"
# pgp
\bgnupg\.net/pks/lookup[?&=0-9a-zA-Z]*
# Spotify
\bopen\.spotify\.com/embed/playlist/\w+
# Mastodon
\bmastodon\.[-a-z.]*/(?:media/|\@)[?&=0-9a-zA-Z_]*
# scastie
\bscastie\.scala-lang\.org/[^/]+/\w+
# images.unsplash.com
\bimages\.unsplash\.com/(?:(?:flagged|reserve)/|)[-\w./%?=%&.;]+
# pastebin
\bpastebin\.com/[\w/]+
# heroku
\b\w+\.heroku\.com/source/archive/\w+
# quip
\b\w+\.quip\.com/\w+(?:(?:#|/issues/)\w+)?
# badgen.net
\bbadgen\.net/badge/[^")\]'\s]+
# statuspage.io
\w+\.statuspage\.io\b
# media.giphy.com
\bmedia\.giphy\.com/media/[^/]+/[\w.?&=]+
# tinyurl
\btinyurl\.com/\w+
# getopts
\bgetopts\s+(?:"[^"]+"|'[^']+')
# ANSI color codes
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
# URL escaped characters
\%[0-9A-F][A-F]
# IPv6
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
# c99 hex digits (not the full format, just one I've seen)
0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP]
# Punycode
\bxn--[-0-9a-z]+
# sha
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
# sha-... -- uses a fancy capture
(['"]|&quot;)[0-9a-f]{40,}\g{-1}
# hex runs
\b[0-9a-fA-F]{16,}\b
# hex in url queries
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
# ssh
(?:ssh-\S+|-nistp256) [-a-zA-Z=;:/0-9+]{12,}
# PGP
\b(?:[0-9A-F]{4} ){9}[0-9A-F]{4}\b
# GPG keys
\b(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}\b
# Well known gpg keys
.well-known/openpgpkey/[\w./]+
# uuid:
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
# hex digits including css/html color classes:
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
# integrity
integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}"
# https://www.gnu.org/software/groff/manual/groff.html
# man troff content
\\f[BCIPR]
# '
\\\(aq
# .desktop mime types
^MimeTypes?=.*$
# .desktop localized entries
^[A-Z][a-z]+\[[a-z]+\]=.*$
# Localized .desktop content
Name\[[^\]]+\]=.*
# IServiceProvider
\bI(?=(?:[A-Z][a-z]{2,})+\b)
# crypt
"\$2[ayb]\$.{56}"
# scrypt / argon
\$(?:scrypt|argon\d+[di]*)\$\S+
# Input to GitHub JSON
content: "[-a-zA-Z=;:/0-9+]*="
# Python stringprefix / binaryprefix
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
(?<!')\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
# Regular expressions for (P|p)assword
\([A-Z]\|[a-z]\)[a-z]+
# JavaScript regular expressions
# javascript test regex
/.*/[gim]*\.test\(
# javascript match regex
\.match\(/[^/\s"]*/[gim]*\s*
# javascript match regex
\.match\(/\\[b].*?/[gim]*\s*\)(?:;|$)
# javascript regex
^\s*/\\[b].*/[gim]*\s*(?:\)(?:;|$)|,$)
# javascript replace regex
\.replace\(/[^/\s"]*/[gim]*\s*,
# Go regular expressions
regexp?\.MustCompile\(`[^`]*`\)
# sed regular expressions
sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2}
# go install
go install(?:\s+[a-z]+\.[-@\w/.]+)+
# kubernetes pod status lists
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
# kubectl - pods in CrashLoopBackOff
\w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+
# kubernetes object suffix
-[0-9a-f]{10}-\w{5}\s
# posthog secrets
posthog\.init\((['"])phc_[^"',]+\g{-1},
# xcode
# xcodeproject scenes
(?:Controller|ID|id)="\w{3}-\w{2}-\w{3}"
# xcode api botches
customObjectInstantitationMethod
# font awesome classes
\.fa-[-a-z0-9]+
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
## You could manually change `(?i)X...` to use `[Xx]...`
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
# Lorem
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
# Non-English
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
# French
# This corpus only had capital letters, but you probably want lowercase ones as well.
\b[LN]'+[a-z]{2,}\b
# latex
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
# the negative lookahead here is to allow catching 'templatesz' as a misspelling
# but to otherwise recognize a Windows path with \templates\foo.template or similar:
\\(?:necessary|r(?:eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z])
# ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b
# Note that the next example is no longer necessary if you are using
# to match a string starting with a `#`, use a character-class:
[#]backwards
# version suffix <word>v#
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
# Compiler flags (Scala)
(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
# Compiler flags
#(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
# Compiler flags (linker)
,-B
# curl arguments
\b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
# set arguments
\bset(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
# tar arguments
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
# macOS temp folders
/var/folders/\w\w/[+\w]+/(?:T|-Caches-)/

View File

@@ -1,117 +0,0 @@
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes
(?:(?i)\.png$)
(?:^|/)(?i)COPYRIGHT
(?:^|/)(?i)LICEN[CS]E
(?:^|/)3rdparty/
(?:^|/)dirs$
(?:^|/)go\.mod$
(?:^|/)go\.sum$
(?:^|/)package(?:-lock|)\.json$
(?:^|/)sources(?:|\.dep)$
(?:^|/)vendor/
\.a$
\.ai$
\.avi$
\.bmp$
\.bz2$
\.cer$
\.class$
\.crl$
\.crt$
\.csr$
\.dll$
\.docx?$
\.drawio$
\.DS_Store$
\.eot$
\.eps$
\.exe$
\.gif$
\.gitattributes$
\.graffle$
\.gz$
\.icns$
\.ico$
\.jar$
\.jks$
\.jpeg$
\.jpg$
\.key$
\.lib$
\.lock$
\.map$
\.min\..
\.mod$
\.mp3$
\.mp4$
\.o$
\.ocf$
\.otf$
\.pbxproj$
\.pdf$
\.pem$
\.png$
\.psd$
\.pyc$
\.runsettings$
\.s$
\.sig$
\.so$
\.svg$
\.svgz$
\.svgz?$
\.tar$
\.tgz$
\.tiff?$
\.ttf$
\.vsdx$
\.wav$
\.webm$
\.webp$
\.woff
\.woff2?$
\.xcf$
\.xls
\.xlsx?$
\.xpm$
\.yml$
\.zip$
^\.github/actions/spelling/
^\.github/fabricbot.json$
^\.gitignore$
^\Q.git-blame-ignore-revs\E$
^\Q.github/workflows/spelling.yml\E$
^\Qdoc/reference/windows-terminal-logo.ans\E$
^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$
^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$
^\Qsrc/host/ft_host/chafa.txt\E$
^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$
^\XamlStyler.json$
^build/config/
^consolegit2gitfilters\.json$
^dep/
^doc/reference/master-sequence-list.csv$
^doc/reference/UTF8-torture-test\.txt$
^oss/
^src/host/ft_uia/run\.bat$
^src/host/runft\.bat$
^src/host/runut\.bat$
^src/interactivity/onecore/BgfxEngine\.
^src/renderer/atlas/
^src/renderer/wddmcon/WddmConRenderer\.
^src/terminal/adapter/ut_adapter/run\.bat$
^src/terminal/parser/delfuzzpayload\.bat$
^src/terminal/parser/ft_fuzzer/run\.bat$
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
^src/terminal/parser/ft_fuzzwrapper/run\.bat$
^src/terminal/parser/ut_parser/Base64Test.cpp$
^src/terminal/parser/ut_parser/run\.bat$
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
^src/tools/lnkd/lnkd\.bat$
^src/tools/pixels/pixels\.bat$
^src/tools/texttests/fira\.txt$
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
^src/types/ut_types/UtilsTests.cpp$
^tools/ReleaseEngineering/ServicingPipeline.ps1$
ignore$
SUMS$

View File

@@ -1,13 +0,0 @@
The contents of each `.txt` file in this directory are merged together.
* [alphabet](alphabet.txt) is a sample for alphabet related items
* [web](web.txt) is a sample for web/html related items
* [expect](expect.txt) is the main list of expected items -- there is nothing
particularly special about the file name (beyond the extension which is
important).
These terms are things which temporarily exist in the project, but which
aren't necessarily words.
If something is a word that could come and go, it probably belongs in a
[dictionary](../dictionary/README.md).

View File

@@ -1,40 +0,0 @@
AAAa
AAAAA
AAAAAAAAAAAAA
AAAAAABBBBBBCCC
AAAAABBBBBBCCC
abcd
abcd
ABCDEFGHIJ
abcdefghijk
ABCDEFGHIJKLMNO
abcdefghijklmnop
ABCDEFGHIJKLMNOPQRST
ABCG
ABE
abf
BBBBB
BBBBBBBB
BBBBBCCC
BBBBCCCCC
BBGGRR
EFG
EFGh
QQQQQQQQQQABCDEFGHIJ
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
qrstuvwxyz
qwerty
qwertyuiopasdfg
YYYYYYYDDDDDDDDDDD
ZAAZZ
ZABBZ
ZBAZZ
ZBBBZ
ZBBZZ
ZYXWVUT
ZZBBZ
ZZZBB
ZZZBZ
ZZZZZ

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +0,0 @@
WCAG
winui
appshellintegration
mdtauk
gfycat
Guake

View File

@@ -1,62 +0,0 @@
# reject `m_data` as there's a certain OS which has evil defines that break things if it's used elsewhere
# \bm_data\b
# If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test,
# you might not want to check in code where you were debugging w/ `fit()`, in which case, you might want
# to use this:
#\bfit\(
# s.b. GitHub
\bGithub\b
# s.b. GitLab
\bGitlab\b
# s.b. JavaScript
\bJavascript\b
# s.b. Microsoft
\bMicroSoft\b
# s.b. another
\ban[- ]other\b
# s.b. greater than
\bgreater then\b
# s.b. into
#\sin to\s
# s.b. opt-in
\sopt in\s
# s.b. less than
\bless then\b
# s.b. otherwise
\bother[- ]wise\b
# s.b. nonexistent
\bnon existing\b
\b[Nn]o[nt][- ]existent\b
# s.b. preexisting
[Pp]re[- ]existing
# s.b. preempt
[Pp]re[- ]empt\b
# s.b. preemptively
[Pp]re[- ]emptively
# s.b. reentrancy
[Rr]e[- ]entrancy
# s.b. reentrant
[Rr]e[- ]entrant
# s.b. workaround(s)
#\bwork[- ]arounds?\b
# Reject duplicate words
\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s

View File

@@ -1,2 +0,0 @@
\\native(?![a-z])
\\nihilist(?![a-z])

View File

@@ -1,8 +0,0 @@
\\registry(?![a-z])
\\release(?![a-z])
\\resources?(?![a-z])
\\result(?![a-z])
\\resultmacros(?![a-z])
\\rules(?![a-z])
\\renderer(?![a-z])
\\rectread(?![a-z])

View File

@@ -1,13 +0,0 @@
\\telemetry(?![a-z])
\\templates(?![a-z])
\\term(?![a-z])
\\terminal(?![a-z])
\\terminalcore(?![a-z])
\\terminalinput(?![a-z])
\\testlist(?![a-z])
\\testmd(?![a-z])
\\testpasses(?![a-z])
\\tests(?![a-z])
\\thread(?![a-z])
\\tools(?![a-z])
\\types(?![a-z])

View File

@@ -1,16 +0,0 @@
The contents of each `.txt` file in this directory are merged together.
Each line is a Perl 5 regular expression.
Nothing is guaranteed about the order in which they're merged.
-- If this is a problem, please reach out.
Note: order of the contents of these files can matter.
Lines from an individual file are handled in file order.
Files are selected in alphabetical order.
* [n](0_n.txt), [r](0_r.txt), and [t](0_t.txt) are specifically to work around
a quirk in the spell checker:
it often sees C strings of the form "Hello\nwerld". And would prefer to
spot the typo of `werld`.
* [patterns](patterns.txt) is the main list -- there is nothing
particularly special about the file name (beyond the extension which is
important).

View File

@@ -1,96 +0,0 @@
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
https?://\S+
[Pp]ublicKeyToken="?[0-9a-fA-F]{16}"?
(?:[{"]|UniqueIdentifier>)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|</UniqueIdentifier)
(?:0[Xx]|\\x|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]{0,2}\b
microsoft/cascadia-code\@[0-9a-fA-F]{40}
\d+x\d+Logo
Scro\&ll
# selectionInput.cpp
:\\windows\\syste\b
TestUtils::VerifyExpectedString\(tb, L"[^"]+"
(?:hostSm|mach)\.ProcessString\(L"[^"]+"
\b([A-Za-z])\g{-1}{3,}\b
0x[0-9A-Za-z]+
Base64::s_(?:En|De)code\(L"[^"]+"
VERIFY_ARE_EQUAL\(L"[^"]+"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\+/"
std::memory_order_[\w]+
D2DERR_SHADER_COMPILE_FAILED
TIL_FEATURE_[0-9A-Z_]+
vcvars\w*
ROY\sG\.\sBIV
!(?:(?i)ESC)!\[
!(?:(?i)CSI)!(?:\d+(?:;\d+|)m|[ABCDF])
# Python stringprefix / binaryprefix
\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'
# Automatically suggested patterns
# hit-count: 3831 file-count: 582
# IServiceProvider
\bI(?=(?:[A-Z][a-z]{2,})+\b)
# hit-count: 71 file-count: 35
# Compiler flags
(?:^|[\t ,"'`=(])-[D](?=[A-Z]{2,}|[A-Z][a-z])
(?:^|[\t ,"'`=(])-[X](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
# hit-count: 41 file-count: 28
# version suffix <word>v#
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
# hit-count: 20 file-count: 9
# hex runs
\b[0-9a-fA-F]{16,}\b
# hit-count: 10 file-count: 7
# uuid:
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
# hit-count: 4 file-count: 4
# mailto urls
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
# hit-count: 4 file-count: 1
# ANSI color codes
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
# hit-count: 2 file-count: 1
# latex
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
# hit-count: 1 file-count: 1
# hex digits including css/html color classes:
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
# hit-count: 1 file-count: 1
# Non-English
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
# hit-count: 1 file-count: 1
# French
# This corpus only had capital letters, but you probably want lowercase ones as well.
\b[LN]'+[a-z]{2,}\b
# acceptable duplicates
# ls directory listings
[-bcdlpsw](?:[-r][-w][-sx]){3}\s+\d+\s+(\S+)\s+\g{-1}\s+\d+\s+
# C/idl types + English ...
\s(Guid|long|LONG|that) \g{-1}\s
# javadoc / .net
(?:[\\@](?:groupname|param)|(?:public|private)(?:\s+static|\s+readonly)*)\s+(\w+)\s+\g{-1}\s
# Commit message -- Signed-off-by and friends
^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$
# Autogenerated revert commit message
^This reverts commit [0-9a-f]{40}\.$
# vtmode
--vtmode\s+(\w+)\s+\g{-1}\s
# ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b

View File

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

View File

@@ -1,134 +0,0 @@
# spelling.yml is blocked per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-g86g-chm8-7r2p
name: Spell checking
# Comment management is handled through a secondary job, for details see:
# https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions
#
# `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment
# (in odd cases, it might actually run just to collapse a commment, but that's fairly rare)
# it needs `contents: write` in order to add a comment.
#
# `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment
# or collapse a comment (in the case where it had previously made a comment and now no longer needs to show a comment)
# it needs `pull-requests: write` in order to manipulate those comments.
# Updating pull request branches is managed via comment handling.
# For details, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-expect-list
#
# These elements work together to make it happen:
#
# `on.issue_comment`
# This event listens to comments by users asking to update the metadata.
#
# `jobs.update`
# This job runs in response to an issue_comment and will push a new commit
# to update the spelling metadata.
#
# `with.experimental_apply_changes_via_bot`
# Tells the action to support and generate messages that enable it
# to make a commit to update the spelling metadata.
#
# `with.ssh_key`
# In order to trigger workflows when the commit is made, you can provide a
# secret (typically, a write-enabled github deploy key).
#
# For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key
on:
push:
branches:
- "**"
tags-ignore:
- "**"
pull_request_target:
branches:
- "**"
tags-ignore:
- "**"
types:
- 'opened'
- 'reopened'
- 'synchronize'
issue_comment:
types:
- 'created'
jobs:
spelling:
name: Spell checking
permissions:
contents: read
pull-requests: read
actions: read
outputs:
followup: ${{ steps.spelling.outputs.followup }}
runs-on: ubuntu-latest
if: "contains(github.event_name, 'pull_request') || github.event_name == 'push'"
concurrency:
group: spelling-${{ github.event.pull_request.number || github.ref }}
# note: If you use only_check_changed_files, you do not want cancel-in-progress
cancel-in-progress: true
steps:
- name: check-spelling
id: spelling
uses: check-spelling/check-spelling@v0.0.21
with:
suppress_push_for_open_pull_request: 1
checkout: true
check_file_names: 1
spell_check_this: check-spelling/spell-check-this@prerelease
post_comment: 0
use_magic_file: 1
extra_dictionary_limit: 10
extra_dictionaries:
cspell:software-terms/src/software-terms.txt
cspell:python/src/python/python-lib.txt
cspell:node/node.txt
cspell:cpp/src/stdlib-c.txt
cspell:cpp/src/stdlib-cpp.txt
cspell:fullstack/fullstack.txt
cspell:filetypes/filetypes.txt
cspell:html/html.txt
cspell:cpp/src/compiler-msvc.txt
cspell:python/src/common/extra.txt
cspell:powershell/powershell.txt
cspell:aws/aws.txt
cspell:cpp/src/lang-keywords.txt
cspell:npm/npm.txt
cspell:dotnet/dotnet.txt
cspell:python/src/python/python.txt
cspell:css/css.txt
cspell:cpp/src/stdlib-cmath.txt
check_extra_dictionaries: ''
comment-push:
name: Report (Push)
# If your workflow isn't running on push, you can remove this job
runs-on: ubuntu-latest
needs: spelling
permissions:
contents: write
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
steps:
- name: comment
uses: check-spelling/check-spelling@v0.0.21
with:
checkout: true
spell_check_this: check-spelling/spell-check-this@prerelease
task: ${{ needs.spelling.outputs.followup }}
comment-pr:
name: Report (PR)
# If you workflow isn't running on pull_request*, you can remove this job
runs-on: ubuntu-latest
needs: spelling
permissions:
pull-requests: write
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
steps:
- name: comment
uses: check-spelling/check-spelling@v0.0.21
with:
checkout: true
spell_check_this: check-spelling/spell-check-this@prerelease
task: ${{ needs.spelling.outputs.followup }}

4
.gitignore vendored
View File

@@ -144,13 +144,13 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to check in your web deploy settings
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# check in your Azure Web App publish settings, but sensitive information contained
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Modules>
<Module name="Microsoft.WindowsTerminal" tdbuildteamid="7105">
<File location="TerminalApp"
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\TerminalApp\Resources\en-US\Resources.resw" />
<File location="TerminalControl"
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\TerminalControl\Resources\en-US\Resources.resw" />
<File location="TerminalConnection"
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\TerminalConnection\Resources\en-US\Resources.resw" />
<File location="WindowsTerminalUniversal"
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\WindowsTerminalUniversal\Resources\en-US\Resources.resw" />
<File location="CascadiaPackage"
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\CascadiaPackage\Resources\en-US\Resources.resw" />
</Module>
</Modules>

View File

@@ -42,7 +42,7 @@ Properties listed below are specific to each unique profile.
| `cursorColor` | Optional | String | | Sets the cursor color of the profile. Overrides `cursorColor` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
| `cursorHeight` | Optional | Integer | | Sets the percentage height of the cursor starting from the bottom. Only works when `cursorShape` is set to `"vintage"`. Accepts values from 25-100. |
| `cursorShape` | Optional | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( &#x2583; ), `"bar"` ( &#x2503; ), `"underscore"` ( &#x2581; ), `"filledBox"` ( &#x2588; ), `"emptyBox"` ( &#x25AF; ) |
| `fontFace` | Optional | String | `Cascadia Code` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. |
| `fontFace` | Optional | String | `Consolas` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. |
| `fontSize` | Optional | Integer | `12` | Sets the font size. |
| `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `#rgb` or `"#rrggbb"`. |
| `hidden` | Optional | Boolean | `false` | If set to true, the profile will not appear in the list of profiles. This can be used to hide default profiles and dynamically generated profiles, while leaving them in your settings file. |

View File

@@ -580,7 +580,7 @@
"type": "boolean"
},
"fontFace": {
"default": "Cascadia Code",
"default": "Consolas",
"description": "Name of the font face used in the profile.",
"type": "string"
},

View File

@@ -30,7 +30,7 @@ Conhost already has a module for search. It implements case sensitive or insensi
We will create a `SearchBoxControl` Xaml `UserControl` element. When a search process begins, a `SearchBoxControl` object will be created and attached to `TermControl` root grid. In other words, one SearchBox is added for each `TermControl`. The reasons for this design is:
1. Each `TermControl` object is a Terminal Window and has a individual text buffer. In phase 1 we are going to search within the current terminal text buffer.
1. Each `TermControl` object is a Terminal Window and has a individual text buffer. In phase 1 we are going to search witin the current terminal text buffer.
2. If we put the search box under TerminalApp, then the search can only happen on the current focused Terminal.
3. If the community does not like the current design, we can lift SearchBox to a higher level.

View File

@@ -92,7 +92,7 @@ would quickly become hard to parse and understand for the user.
### Proposal 2 - Commands and Parameters
Instead, we'll try to separate these arguments by their responsibilities. Some
Instead, we'll try to seperate these arguments by their responsibilities. Some
of these arguments cause something to happen, like `help`, `version`, or
`open-settings`. Other arguments act more like modifiers, like for example
`--profile` or `--startingDirectory`, which provide additional information to
@@ -275,7 +275,7 @@ terminal window.
`list-profiles [--all,-A] [--showGuids,-g]`
Displays a list of each of the available profiles. Each profile displays it's
name, separated by newlines.
name, seperated by newlines.
**Parameters**:
* `--all,-A`: Show all profiles, including profiles marked `"hidden": true`.
@@ -323,7 +323,7 @@ vertically or horizontally.
**Parameters**:
* `--target,-t target-pane`: Creates a new split in the given `target-pane`.
Each pane has a unique index (per-tab) which can be used to identify them.
These indices are assigned in the order the panes were created. If omitted,
These indicies are assigned in the order the panes were created. If omitted,
defaults to the index of the currently focused pane.
* `-H`, `-V`: Used to indicate which direction to split the pane. `-V` is
"vertically" (think `[|]`), and `-H` is "horizontally" (think `[-]`). If
@@ -355,7 +355,7 @@ Moves focus within the currently focused tab to a given pane.
* `--target,-t target-pane`: moves focus to the given `target-pane`. Each pane
has a unique index (per-tab) which can be used to identify them. These
indices are assigned in the order the panes were created. If omitted,
indicies are assigned in the order the panes were created. If omitted,
defaults to the index of the currently focused pane (which is effectively a
no-op).
@@ -410,7 +410,7 @@ like `guid` and `name`, as well as high priority properties to add as arguments.
Following an investigation performed the week of Nov 18th, 2019, I've determined
that we should be able to use the [CLI11] open-source library to parse
our arguments. We'll need to add some additional logic on top of CLI11 in order
to properly separate commands with `;`, but that's not impossible to achieve.
to properly seperate commands with `;`, but that's not impossible to achieve.
CLI11 will allow us to parse commandlines as a series of options, with a
possible sub-command that takes its own set of parameters. This functionality

View File

@@ -72,25 +72,12 @@ Assuming that you've installed Git Bash into `C:/Program Files/Git`:
```json
{
"name" : "Git Bash",
"commandline" : "C:/Program Files/Git/bin/bash.exe -li",
"commandline" : "C:/Program Files/Git/bin/bash.exe",
"icon" : "C:/Program Files/Git/mingw64/share/git/git-for-windows.ico",
"startingDirectory" : "%USERPROFILE%"
}
````
## Git Bash (WOW64)
Assuming that you've installed Git Bash into `C:/Program Files (x86)/Git`:
```json
{
"name" : "Git Bash",
"commandline" : "%ProgramFiles(x86)%/Git/bin/bash.exe -li",
"icon" : "%ProgramFiles(x86)%/Git/mingw32/share/git/git-for-windows.ico",
"startingDirectory" : "%USERPROFILE%"
}
```
## MSYS2
Assuming that you've installed MSYS2 into `C:/msys64`:
@@ -104,16 +91,4 @@ Assuming that you've installed MSYS2 into `C:/msys64`:
}
````
## Developer Command Prompt for Visual Studio
Assuming that you've installed VS 2019 Professional:
```json
{
"name" : "Developer Command Prompt for VS 2019",
"commandline" : "cmd.exe /k \"C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/Common7/Tools/VsDevCmd.bat\"",
"startingDirectory" : "%USERPROFILE%"
}
```
<!-- Adding a tool here? Make sure to add it in alphabetical order! -->

View File

@@ -107,22 +107,6 @@ add the following to your keybindings:
This will _unbind_ <kbd>Ctrl+Shift+6</kbd>, allowing vim to use the keystroke
instead of the terminal.
### Binding multiple keys
You can have multiple key chords bound to the same action. To do this, simply
add multiple bindings for the same action. For example:
```json
"keybindings" :
[
{ "command": "copy", "keys": "ctrl+shift+c" },
{ "command": "copy", "keys": "ctrl+c" },
{ "command": "copy", "keys": "enter" }
]
```
In this snippet, all three of <kbd>ctrl+shift+c</kbd>, <kbd>ctrl+c</kbd> and <kbd>enter</kbd> are bound to `copy`.
## Profiles
A profile contains the settings applied when a new WT tab is opened. Each
@@ -224,7 +208,7 @@ scheme in `profiles.json` with the same name as a default color scheme.
If you'd like to unbind a keystroke that's bound to an action in the default
keybindings, you can set the `"command"` to `"unbound"` or `null`. This will
allow the keystroke to fallthrough to the commandline application instead of
allow the keystroke to fallthough to the commandline application instead of
performing the default action.
### Dynamic Profiles

View File

@@ -64,7 +64,7 @@ OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute&
// Routine Description:
// - This is a fill-mode iterator for one particular CHAR_INFO. It will repeat forever if fillLimit is 0.
// Arguments:
// - charInfo - The legacy character and color data to use for filling (uses Unicode portion of text data)
// - charInfo - The legacy character and color data to use for fililng (uses Unicode portion of text data)
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) noexcept :
_mode(Mode::Fill),

View File

@@ -43,7 +43,7 @@
<!-- Resources -->
<!-- This resw only defines things that are used in this package's AppxManifest,
so it's not in the common resource items. -->
<PRIResource Include="Resources\*\Resources.resw" />
<PRIResource Include="Resources\Resources.language-en.resw" />
<PRIResource Include="Resources\Resources.resw" />
</ItemGroup>
<PropertyGroup Condition="'$(WindowsTerminalReleaseBuild)'!='true'">

View File

@@ -70,10 +70,6 @@ namespace TerminalAppLocalTests
TEST_METHOD(TestTerminalArgsForBinding);
TEST_METHOD(FindMissingProfile);
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
TEST_METHOD(TestLayerProfileOnColorScheme);
TEST_METHOD(ValidateKeybindingsWarnings);
@@ -2098,146 +2094,6 @@ namespace TerminalAppLocalTests
}
}
void SettingsTests::FindMissingProfile()
{
// Test that CascadiaSettings::FindProfile returns null for a GUID that
// doesn't exist
const std::string settingsString{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
}
]
})" };
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
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 Profile* const profile1 = settings->FindProfile(guid1);
const Profile* const profile2 = settings->FindProfile(guid2);
const Profile* const profile3 = settings->FindProfile(guid3);
VERIFY_IS_NOT_NULL(profile1);
VERIFY_IS_NOT_NULL(profile2);
VERIFY_IS_NULL(profile3);
VERIFY_ARE_EQUAL(L"profile0", profile1->GetName());
VERIFY_ARE_EQUAL(L"profile1", profile2->GetName());
}
void SettingsTests::MakeSettingsForProfileThatDoesntExist()
{
// Test that MakeSettings throws when the GUID doesn't exist
const std::string settingsString{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"historySize": 1
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"historySize": 2
}
]
})" };
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
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}");
try
{
auto terminalSettings = settings->BuildSettings(guid1);
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
}
try
{
auto terminalSettings = settings->BuildSettings(guid2);
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(2, terminalSettings.HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
}
VERIFY_THROWS(auto terminalSettings = settings->BuildSettings(guid3), wil::ResultException, L"This call to BuildSettings should fail");
try
{
const auto [guid, termSettings] = settings->BuildSettings(nullptr);
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
}
}
void SettingsTests::MakeSettingsForDefaultProfileThatDoesntExist()
{
// Test that MakeSettings _doesnt_ throw when we load settings with a
// defaultProfile that's not in the list, we validate the settings, and
// then call MakeSettings(nullopt). The validation should ensure that
// the default profile is something reasonable
const std::string settingsString{ R"(
{
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"historySize": 1
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"historySize": 2
}
]
})" };
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
settings->_ValidateSettings();
VERIFY_ARE_EQUAL(2u, settings->_warnings.size());
VERIFY_ARE_EQUAL(2u, settings->_profiles.size());
VERIFY_ARE_EQUAL(settings->_globals.GetDefaultProfile(), settings->_profiles.at(0).GetGuid());
try
{
const auto [guid, termSettings] = settings->BuildSettings(nullptr);
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to BuildSettings should succeed");
}
}
void SettingsTests::TestLayerProfileOnColorScheme()
{
Log::Comment(NoThrowString().Format(

View File

@@ -13,10 +13,8 @@
using namespace Microsoft::Console;
using namespace TerminalApp;
using namespace winrt::TerminalApp;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;
namespace TerminalAppLocalTests
{
@@ -55,20 +53,11 @@ namespace TerminalAppLocalTests
TEST_METHOD(CreateSimpleTerminalXamlType);
TEST_METHOD(CreateTerminalMuxXamlType);
TEST_METHOD(CreateTerminalPage);
TEST_METHOD(TryDuplicateBadTab);
TEST_METHOD(TryDuplicateBadPane);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
return true;
}
private:
void _initializeTerminalPage(winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage>& page,
std::shared_ptr<CascadiaSettings> initialSettings);
};
void TabTests::EnsureTestsActivate()
@@ -97,8 +86,8 @@ namespace TerminalAppLocalTests
// Just creating it is enough to know that everything is working.
winrt::Microsoft::Terminal::TerminalConnection::EchoConnection conn{};
VERIFY_IS_NOT_NULL(conn);
// We're doing this test separately from the TryCreateSettingsType test,
// to ensure both dependent binaries (TerminalSettings and
// We're doing this test seperately from the TryCreateSettingsType test,
// to ensure both dependent binaries (TemrinalSettings and
// TerminalConnection) both work individually.
}
@@ -175,321 +164,4 @@ namespace TerminalAppLocalTests
VERIFY_SUCCEEDED(result);
}
void TabTests::CreateTerminalPage()
{
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
auto result = RunOnUIThread([&page]() {
page = winrt::make_self<winrt::TerminalApp::implementation::TerminalPage>();
VERIFY_IS_NOT_NULL(page);
});
VERIFY_SUCCEEDED(result);
}
// Method Description:
// - This is a helper to set up a TerminalPage for a unittest. This method
// does a couple things:
// * Create()'s a TerminalPage with the given settings. Constructing a
// TerminalPage so that we can get at its implementation is wacky, so
// this helper will do it correctly for you, even if this doesn't make a
// ton of sense on the surface. This is also why you need to pass both a
// projection and a com_ptr to this method.
// * It will use the provided settings object to initialize the TerminalPage
// * It will add the TerminalPage to the test Application, so that we can
// get actual layout events. Much of the Terminal assumes there's a
// non-zero ActualSize to the Terminal window, and adding the Page to
// the Application will make it behave as expected.
// * It will wait for the TerminalPage to finish initialization before
// returning control to the caller. It does this by creating an event and
// only setting the event when the TerminalPage raises its Initialized
// event, to signal that startup is complete. At this point, there will
// be one tab with the default profile in the page.
// * It will also ensure that the first tab is focused, since that happens
// asynchronously in the application typically.
// Arguments:
// - page: a TerminalPage implementation ptr that will receive the new TerminalPage instance
// - initialSettings: a CascadiaSettings to initialize the TerminalPage with.
// Return Value:
// - <none>
void TabTests::_initializeTerminalPage(winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage>& page,
std::shared_ptr<CascadiaSettings> initialSettings)
{
// This is super wacky, but we can't just initialize the
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
// during TerminalPage::Create() below.
//
// Instead, create the winrt object, then get a com_ptr to the
// implementation _from_ the winrt object. This seems to work, even if
// it's weird.
winrt::TerminalApp::TerminalPage projectedPage{ nullptr };
Log::Comment(NoThrowString().Format(L"Construct the TerminalPage"));
auto result = RunOnUIThread([&projectedPage, &page, initialSettings]() {
projectedPage = winrt::TerminalApp::TerminalPage();
page.copy_from(winrt::get_self<winrt::TerminalApp::implementation::TerminalPage>(projectedPage));
page->_settings = initialSettings;
});
VERIFY_SUCCEEDED(result);
VERIFY_IS_NOT_NULL(page);
VERIFY_IS_NOT_NULL(page->_settings);
::details::Event waitForInitEvent;
if (!waitForInitEvent.IsValid())
{
VERIFY_SUCCEEDED(HRESULT_FROM_WIN32(::GetLastError()));
}
page->Initialized([&waitForInitEvent](auto&&, auto&&) {
waitForInitEvent.Set();
});
Log::Comment(L"Create() the TerminalPage");
result = RunOnUIThread([&page]() {
VERIFY_IS_NOT_NULL(page);
VERIFY_IS_NOT_NULL(page->_settings);
page->Create();
Log::Comment(L"Create()'d the page successfully");
auto app = ::winrt::Windows::UI::Xaml::Application::Current();
winrt::TerminalApp::TerminalPage pp = *page;
winrt::Windows::UI::Xaml::Window::Current().Content(pp);
winrt::Windows::UI::Xaml::Window::Current().Activate();
});
VERIFY_SUCCEEDED(result);
Log::Comment(L"Wait for the page to finish initializing...");
VERIFY_SUCCEEDED(waitForInitEvent.Wait());
Log::Comment(L"...Done");
result = RunOnUIThread([&page]() {
// In the real app, this isn't a problem, but doesn't happen
// reliably in the unit tests.
Log::Comment(L"Ensure we set the first tab as the selected one.");
auto tab{ page->_GetStrongTabImpl(0) };
page->_tabView.SelectedItem(tab->GetTabViewItem());
page->_UpdatedSelectedTab(0);
});
VERIFY_SUCCEEDED(result);
}
void TabTests::TryDuplicateBadTab()
{
// * Create a tab with a profile with GUID 1
// * Reload the settings so that GUID 1 is no longer in the list of profiles
// * Try calling _DuplicateTabViewItem on tab 1
// * No new tab should be created (and more importantly, the app should not crash)
//
// Created to test GH#2455
const std::string settingsJson0{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"historySize": 1
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"historySize": 2
}
]
})" };
const std::string settingsJson1{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"historySize": 2
}
]
})" };
VerifyParseSucceeded(settingsJson0);
auto settings0 = std::make_shared<CascadiaSettings>(false);
VERIFY_IS_NOT_NULL(settings0);
settings0->_ParseJsonString(settingsJson0, false);
settings0->LayerJson(settings0->_userSettings);
settings0->_ValidateSettings();
VerifyParseSucceeded(settingsJson1);
auto settings1 = std::make_shared<CascadiaSettings>(false);
VERIFY_IS_NOT_NULL(settings1);
settings1->_ParseJsonString(settingsJson1, false);
settings1->LayerJson(settings1->_userSettings);
settings1->_ValidateSettings();
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
// This is super wacky, but we can't just initialize the
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
// during TerminalPage::Create() below.
//
// Instead, create the winrt object, then get a com_ptr to the
// implementation _from_ the winrt object. This seems to work, even if
// it's weird.
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
_initializeTerminalPage(page, settings0);
auto result = RunOnUIThread([&page]() {
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
});
VERIFY_SUCCEEDED(result);
Log::Comment(L"Duplicate the first tab");
result = RunOnUIThread([&page]() {
page->_DuplicateTabViewItem();
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
});
VERIFY_SUCCEEDED(result);
Log::Comment(NoThrowString().Format(
L"Change the settings of the TerminalPage so the first profile is "
L"no longer in the list of profiles"));
result = RunOnUIThread([&page, settings1]() {
page->_settings = settings1;
});
VERIFY_SUCCEEDED(result);
Log::Comment(L"Duplicate the tab, and don't crash");
result = RunOnUIThread([&page]() {
page->_DuplicateTabViewItem();
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
});
VERIFY_SUCCEEDED(result);
}
void TabTests::TryDuplicateBadPane()
{
// * Create a tab with a profile with GUID 1
// * Reload the settings so that GUID 1 is no longer in the list of profiles
// * Try calling _SplitPane(Duplicate) on tab 1
// * No new pane should be created (and more importantly, the app should not crash)
//
// Created to test GH#2455
const std::string settingsJson0{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"historySize": 1
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"historySize": 2
}
]
})" };
const std::string settingsJson1{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"historySize": 2
}
]
})" };
VerifyParseSucceeded(settingsJson0);
auto settings0 = std::make_shared<CascadiaSettings>(false);
VERIFY_IS_NOT_NULL(settings0);
settings0->_ParseJsonString(settingsJson0, false);
settings0->LayerJson(settings0->_userSettings);
settings0->_ValidateSettings();
VerifyParseSucceeded(settingsJson1);
auto settings1 = std::make_shared<CascadiaSettings>(false);
VERIFY_IS_NOT_NULL(settings1);
settings1->_ParseJsonString(settingsJson1, false);
settings1->LayerJson(settings1->_userSettings);
settings1->_ValidateSettings();
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
// This is super wacky, but we can't just initialize the
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
// the lambda. We'll crash trying to get a weak_ref to the TerminalPage
// during TerminalPage::Create() below.
//
// Instead, create the winrt object, then get a com_ptr to the
// implementation _from_ the winrt object. This seems to work, even if
// it's weird.
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
_initializeTerminalPage(page, settings0);
auto result = RunOnUIThread([&page]() {
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
});
VERIFY_SUCCEEDED(result);
result = RunOnUIThread([&page]() {
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(1, tab->_GetLeafPaneCount());
});
VERIFY_SUCCEEDED(result);
Log::Comment(NoThrowString().Format(L"Duplicate the first pane"));
result = RunOnUIThread([&page]() {
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, tab->_GetLeafPaneCount());
});
VERIFY_SUCCEEDED(result);
Log::Comment(NoThrowString().Format(
L"Change the settings of the TerminalPage so the first profile is "
L"no longer in the list of profiles"));
result = RunOnUIThread([&page, settings1]() {
page->_settings = settings1;
});
VERIFY_SUCCEEDED(result);
Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash"));
result = RunOnUIThread([&page]() {
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2,
tab->_GetLeafPaneCount(),
L"We should gracefully do nothing here - the profile no longer exists.");
});
VERIFY_SUCCEEDED(result);
auto cleanup = wil::scope_exit([] {
auto result = RunOnUIThread([]() {
// There's something causing us to crash north of
// TSFInputControl::NotifyEnter, or LayoutRequested. It's very
// unclear what that issue is. Since these tests don't run in
// CI, simply log a message so that the dev running these tests
// knows it's expected.
Log::Comment(L"This test often crashes on cleanup, even when it succeeds. If it succeeded, then crashes, that's okay.");
});
VERIFY_SUCCEEDED(result);
});
}
}

View File

@@ -89,8 +89,8 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
}
HwndTerminal::HwndTerminal(HWND parentHwnd) :
_desiredFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8 },
_actualFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8, false },
_desiredFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8 },
_actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8, false },
_uiaProvider{ nullptr },
_uiaProviderInitialized{ false },
_currentDpi{ USER_DEFAULT_SCREEN_DPI },

View File

@@ -9,7 +9,7 @@
using namespace winrt::TerminalApp;
using namespace TerminalApp;
// Either a ; at the start of a line, or a ; preceded by any non-\ char.
// Either a ; at the start of a line, or a ; preceeded by any non-\ char.
const std::wregex AppCommandlineArgs::_commandDelimiterRegex{ LR"(^;|[^\\];)" };
AppCommandlineArgs::AppCommandlineArgs()
@@ -172,7 +172,7 @@ void AppCommandlineArgs::_buildNewTabParser()
// that `this` will still be safe - this function just lets us know this
// command was parsed.
_newTabCommand.subcommand->callback([&, this]() {
// Build the NewTab action from the values we've parsed on the commandline.
// Buld the NewTab action from the values we've parsed on the commandline.
auto newTabAction = winrt::make_self<implementation::ActionAndArgs>();
newTabAction->Action(ShortcutAction::NewTab);
auto args = winrt::make_self<implementation::NewTabArgs>();
@@ -207,7 +207,7 @@ void AppCommandlineArgs::_buildSplitPaneParser()
// that `this` will still be safe - this function just lets us know this
// command was parsed.
_newPaneCommand.subcommand->callback([&, this]() {
// Build the SplitPane action from the values we've parsed on the commandline.
// Buld the SplitPane action from the values we've parsed on the commandline.
auto splitPaneActionAndArgs = winrt::make_self<implementation::ActionAndArgs>();
splitPaneActionAndArgs->Action(ShortcutAction::SplitPane);
auto args = winrt::make_self<implementation::SplitPaneArgs>();
@@ -259,7 +259,7 @@ void AppCommandlineArgs::_buildFocusTabParser()
// that `this` will still be safe - this function just lets us know this
// command was parsed.
_focusTabCommand->callback([&, this]() {
// Build the action from the values we've parsed on the commandline.
// Buld the action from the values we've parsed on the commandline.
auto focusTabAction = winrt::make_self<implementation::ActionAndArgs>();
if (_focusTabIndex >= 0)
@@ -393,7 +393,7 @@ void AppCommandlineArgs::_resetStateToDefault()
// Function Description:
// - Builds a list of Commandline objects for the given argc,argv. Each
// Commandline represents a single command to parse. These commands can be
// separated by ";", which indicates the start of the next commandline. If the
// seperated by ";", which indicates the start of the next commandline. If the
// user would like to provide ';' in the text of the commandline, they can
// escape it as "\;".
// Arguments:
@@ -423,7 +423,7 @@ std::vector<Commandline> AppCommandlineArgs::BuildCommands(winrt::array_view<con
// Function Description:
// - Builds a list of Commandline objects for the given argc,argv. Each
// Commandline represents a single command to parse. These commands can be
// separated by ";", which indicates the start of the next commandline. If the
// seperated by ";", which indicates the start of the next commandline. If the
// user would like to provide ';' in the text of the commandline, they can
// escape it as "\;".
// Arguments:
@@ -457,7 +457,7 @@ std::vector<Commandline> AppCommandlineArgs::BuildCommands(const std::vector<con
// Function Description:
// - Update and append Commandline objects for the given arg to the given list
// of commands. Each Commandline represents a single command to parse. These
// commands can be separated by ";", which indicates the start of the next
// commands can be seperated by ";", which indicates the start of the next
// commandline. If the user would like to provide ';' in the text of the
// commandline, they can escape it as "\;".
// - As we parse arg, if it doesn't contain a delimiter in it, we'll add it to

View File

@@ -344,7 +344,7 @@ static const std::map<ShortcutAction, ParseActionFunction, std::less<>> argParse
// Function Description:
// - Small helper to create a json value serialization of a single
// KeyBinding->Action mapping.
// KeyBinding->Action maping.
// {
// keys:[String],
// command:String

View File

@@ -410,7 +410,7 @@ namespace winrt::TerminalApp::implementation
if (_settings->GlobalSettings().GetShowTabsInTitlebar())
{
// If we're showing the tabs in the titlebar, we need to use a
// TitlebarControl here to calculate how much space to reserve.
// TitlebarContol here to calculate how much space to reserve.
//
// We'll create a fake TitlebarControl, and we'll propose an
// available size to it with Measure(). After Measure() is called,

View File

@@ -177,7 +177,7 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"CustomKeybindings",
TraceLoggingDescription("Event emitted when custom keybindings are identified on load/reload"),
TraceLoggingDescription("Event emitted when custom keybindings are idenfitied on load/reload"),
TraceLoggingUtf8String(keybindingsString.c_str(), "Keybindings", "Keybindings as JSON"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));

View File

@@ -1,147 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "DebugTapConnection.h"
using namespace ::winrt::Microsoft::Terminal::TerminalConnection;
using namespace ::winrt::Windows::Foundation;
namespace winrt::Microsoft::TerminalApp::implementation
{
// DebugInputTapConnection is an implementation detail of DebugTapConnection.
// It wraps the _actual_ connection so it can hook WriteInput and forward it
// into the actual debug panel.
class DebugInputTapConnection : public winrt::implements<DebugInputTapConnection, ITerminalConnection>
{
public:
DebugInputTapConnection(winrt::com_ptr<DebugTapConnection> pairedTap, ITerminalConnection wrappedConnection) :
_pairedTap{ pairedTap },
_wrappedConnection{ std::move(wrappedConnection) }
{
}
~DebugInputTapConnection() = default;
void Start()
{
_wrappedConnection.Start();
}
void WriteInput(hstring const& data)
{
_pairedTap->_PrintInput(data);
_wrappedConnection.WriteInput(data);
}
void Resize(uint32_t rows, uint32_t columns) { _wrappedConnection.Resize(rows, columns); }
void Close() { _wrappedConnection.Close(); }
winrt::event_token TerminalOutput(TerminalOutputHandler const& args) { return _wrappedConnection.TerminalOutput(args); };
void TerminalOutput(winrt::event_token const& token) noexcept { _wrappedConnection.TerminalOutput(token); };
winrt::event_token StateChanged(TypedEventHandler<ITerminalConnection, IInspectable> const& handler) { return _wrappedConnection.StateChanged(handler); };
void StateChanged(winrt::event_token const& token) noexcept { _wrappedConnection.StateChanged(token); };
ConnectionState State() const noexcept { return _wrappedConnection.State(); }
private:
winrt::com_ptr<DebugTapConnection> _pairedTap;
ITerminalConnection _wrappedConnection;
};
DebugTapConnection::DebugTapConnection(ITerminalConnection wrappedConnection)
{
_outputRevoker = wrappedConnection.TerminalOutput(winrt::auto_revoke, { this, &DebugTapConnection::_OutputHandler });
_stateChangedRevoker = wrappedConnection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*e*/) {
_StateChangedHandlers(*this, nullptr);
});
_wrappedConnection = wrappedConnection;
}
DebugTapConnection::~DebugTapConnection()
{
}
void DebugTapConnection::Start()
{
// presume the wrapped connection is started.
}
void DebugTapConnection::WriteInput(hstring const& data)
{
// If the user types into the tap side, forward it to the input side
if (auto strongInput{ _inputSide.get() })
{
auto inputAsTap{ winrt::get_self<DebugInputTapConnection>(strongInput) };
inputAsTap->WriteInput(data);
}
}
void DebugTapConnection::Resize(uint32_t /*rows*/, uint32_t /*columns*/)
{
// no resize events are propagated
}
void DebugTapConnection::Close()
{
_outputRevoker.revoke();
_stateChangedRevoker.revoke();
_wrappedConnection = nullptr;
}
ConnectionState DebugTapConnection::State() const noexcept
{
if (auto strongConnection{ _wrappedConnection.get() })
{
return strongConnection.State();
}
return ConnectionState::Failed;
}
static std::wstring _sanitizeString(const std::wstring_view str)
{
std::wstring newString{ str.begin(), str.end() };
for (auto& ch : newString)
{
if (ch < 0x20)
{
ch += 0x2400;
}
else if (ch == 0x20)
{
ch = 0x2423; // replace space with ␣
}
else if (ch == 0x7f)
{
ch = 0x2421; // replace del with ␡
}
}
return newString;
}
void DebugTapConnection::_OutputHandler(const hstring str)
{
_TerminalOutputHandlers(_sanitizeString(str));
}
// Called by the DebugInputTapConnection to print user input
void DebugTapConnection::_PrintInput(const hstring& str)
{
auto clean{ _sanitizeString(str) };
auto formatted{ wil::str_printf<std::wstring>(L"\x1b[91m%ls\x1b[m", clean.data()) };
_TerminalOutputHandlers(formatted);
}
// Wire us up so that we can forward input through
void DebugTapConnection::SetInputTap(const Microsoft::Terminal::TerminalConnection::ITerminalConnection& inputTap)
{
_inputSide = inputTap;
}
}
// Function Description
// - Takes one connection and returns two connections:
// 1. One that can be used in place of the original connection (wrapped)
// 2. One that will print raw VT sequences sent into and received _from_ the original connection.
std::tuple<ITerminalConnection, ITerminalConnection> OpenDebugTapConnection(ITerminalConnection baseConnection)
{
using namespace winrt::Microsoft::TerminalApp::implementation;
auto debugSide{ winrt::make_self<DebugTapConnection>(baseConnection) };
auto inputSide{ winrt::make_self<DebugInputTapConnection>(debugSide, baseConnection) };
debugSide->SetInputTap(*inputSide);
std::tuple<ITerminalConnection, ITerminalConnection> p{ *inputSide, *debugSide };
return p;
}

View File

@@ -1,42 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
#include "../../inc/cppwinrt_utils.h"
namespace winrt::Microsoft::TerminalApp::implementation
{
class DebugInputTapConnection;
class DebugTapConnection : public winrt::implements<DebugTapConnection, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection>
{
public:
DebugTapConnection(Microsoft::Terminal::TerminalConnection::ITerminalConnection wrappedConnection);
~DebugTapConnection();
void Start();
void WriteInput(hstring const& data);
void Resize(uint32_t rows, uint32_t columns);
void Close();
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState State() const noexcept;
void SetInputTap(const Microsoft::Terminal::TerminalConnection::ITerminalConnection& inputTap);
WINRT_CALLBACK(TerminalOutput, winrt::Microsoft::Terminal::TerminalConnection::TerminalOutputHandler);
TYPED_EVENT(StateChanged, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection, winrt::Windows::Foundation::IInspectable);
private:
void _PrintInput(const hstring& data);
void _OutputHandler(const hstring str);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::TerminalOutput_revoker _outputRevoker;
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection::StateChanged_revoker _stateChangedRevoker;
winrt::weak_ref<Microsoft::Terminal::TerminalConnection::ITerminalConnection> _wrappedConnection;
winrt::weak_ref<Microsoft::Terminal::TerminalConnection::ITerminalConnection> _inputSide;
friend class DebugInputTapConnection;
};
}
std::tuple<winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection> OpenDebugTapConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection baseConnection);

View File

@@ -41,14 +41,6 @@ static constexpr std::wstring_view LightThemeValue{ L"light" };
static constexpr std::wstring_view DarkThemeValue{ L"dark" };
static constexpr std::wstring_view SystemThemeValue{ L"system" };
static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" };
#ifdef _DEBUG
static constexpr bool debugFeaturesDefault{ true };
#else
static constexpr bool debugFeaturesDefault{ false };
#endif
GlobalAppSettings::GlobalAppSettings() :
_keybindings{ winrt::make_self<winrt::TerminalApp::implementation::AppKeyBindings>() },
_keybindingsWarnings{},
@@ -67,8 +59,7 @@ GlobalAppSettings::GlobalAppSettings() :
_tabWidthMode{ TabViewWidthMode::Equal },
_wordDelimiters{ DEFAULT_WORD_DELIMITERS },
_copyOnSelect{ false },
_launchMode{ LaunchMode::DefaultMode },
_debugFeatures{ debugFeaturesDefault }
_launchMode{ LaunchMode::DefaultMode }
{
}
@@ -180,11 +171,6 @@ void GlobalAppSettings::SetConfirmCloseAllTabs(const bool confirmCloseAllTabs) n
_confirmCloseAllTabs = confirmCloseAllTabs;
}
bool GlobalAppSettings::DebugFeaturesEnabled() const noexcept
{
return _debugFeatures;
}
#pragma region ExperimentalSettings
bool GlobalAppSettings::GetShowTabsInTitlebar() const noexcept
{
@@ -251,7 +237,6 @@ Json::Value GlobalAppSettings::ToJson() const
jsonObject[JsonKey(KeybindingsKey)] = _keybindings->ToJson();
jsonObject[JsonKey(ConfirmCloseAllKey)] = _confirmCloseAllTabs;
jsonObject[JsonKey(SnapToGridOnResizeKey)] = _SnapToGridOnResize;
jsonObject[JsonKey(DebugFeaturesKey)] = _debugFeatures;
return jsonObject;
}
@@ -339,9 +324,6 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
}
JsonUtils::GetBool(json, SnapToGridOnResizeKey, _SnapToGridOnResize);
// GetBool will only override the current value if the key exists
JsonUtils::GetBool(json, DebugFeaturesKey, _debugFeatures);
}
// Method Description:

View File

@@ -77,8 +77,6 @@ public:
winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode GetTabWidthMode() const noexcept;
bool DebugFeaturesEnabled() const noexcept;
Json::Value ToJson() const;
static GlobalAppSettings FromJson(const Json::Value& json);
void LayerJson(const Json::Value& json);
@@ -117,8 +115,6 @@ private:
winrt::TerminalApp::LaunchMode _launchMode;
bool _debugFeatures;
static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept;
static std::wstring_view _SerializeTheme(const winrt::Windows::UI::Xaml::ElementTheme theme) noexcept;

View File

@@ -70,7 +70,7 @@ void Pane::ResizeContent(const Size& newSize)
const auto width = newSize.Width;
const auto height = newSize.Height;
_CreateRowColDefinitions();
_CreateRowColDefinitions(newSize);
if (_splitState == SplitState::Vertical)
{
@@ -790,24 +790,23 @@ void Pane::_SetupChildCloseHandlers()
// which is stored in _desiredSplitPosition
// - Does nothing if our split state is currently set to SplitState::None
// Arguments:
// - <none>
// - rootSize: The dimensions in pixels that this pane (and its children should consume.)
// Return Value:
// - <none>
void Pane::_CreateRowColDefinitions()
void Pane::_CreateRowColDefinitions(const Size& rootSize)
{
const auto first = _desiredSplitPosition * 100.0f;
const auto second = 100.0f - first;
if (_splitState == SplitState::Vertical)
{
_root.ColumnDefinitions().Clear();
// Create two columns in this grid: one for each pane
const auto paneSizes = _CalcChildrenSizes(rootSize.Width);
auto firstColDef = Controls::ColumnDefinition();
firstColDef.Width(GridLengthHelper::FromValueAndType(first, GridUnitType::Star));
firstColDef.Width(GridLengthHelper::FromValueAndType(paneSizes.first, GridUnitType::Star));
auto secondColDef = Controls::ColumnDefinition();
secondColDef.Width(GridLengthHelper::FromValueAndType(second, GridUnitType::Star));
secondColDef.Width(GridLengthHelper::FromValueAndType(paneSizes.second, GridUnitType::Star));
_root.ColumnDefinitions().Append(firstColDef);
_root.ColumnDefinitions().Append(secondColDef);
@@ -817,18 +816,35 @@ void Pane::_CreateRowColDefinitions()
_root.RowDefinitions().Clear();
// Create two rows in this grid: one for each pane
const auto paneSizes = _CalcChildrenSizes(rootSize.Height);
auto firstRowDef = Controls::RowDefinition();
firstRowDef.Height(GridLengthHelper::FromValueAndType(first, GridUnitType::Star));
firstRowDef.Height(GridLengthHelper::FromValueAndType(paneSizes.first, GridUnitType::Star));
auto secondRowDef = Controls::RowDefinition();
secondRowDef.Height(GridLengthHelper::FromValueAndType(second, GridUnitType::Star));
secondRowDef.Height(GridLengthHelper::FromValueAndType(paneSizes.second, GridUnitType::Star));
_root.RowDefinitions().Append(firstRowDef);
_root.RowDefinitions().Append(secondRowDef);
}
}
// Method Description:
// - Initializes our UI for a new split in this pane. Sets up row/column
// definitions, and initializes the separator grid. Does nothing if our split
// state is currently set to SplitState::None
// Arguments:
// - <none>
// Return Value:
// - <none>
void Pane::_CreateSplitContent()
{
Size actualSize{ gsl::narrow_cast<float>(_root.ActualWidth()),
gsl::narrow_cast<float>(_root.ActualHeight()) };
_CreateRowColDefinitions(actualSize);
}
// Method Description:
// - Sets the thickness of each side of our borders to match our _borders state.
// Arguments:
@@ -1061,7 +1077,7 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
_control = { nullptr };
_secondChild = std::make_shared<Pane>(profile, control);
_CreateRowColDefinitions();
_CreateSplitContent();
_root.Children().Append(_firstChild->GetRootElement());
_root.Children().Append(_secondChild->GetRootElement());
@@ -1265,7 +1281,7 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const
// already snapped or minimum size.
// Arguments:
// - widthOrHeight: if true operates on width, otherwise on height.
// - sizeNode: a layout size node that corresponds to this pane.
// - sizeNode: a layouting node that corresponds to this pane.
// Return Value:
// - <none>
void Pane::_AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& sizeNode) const
@@ -1506,74 +1522,4 @@ void Pane::_SetupResources()
}
}
int Pane::GetLeafPaneCount() const noexcept
{
return _IsLeaf() ? 1 : (_firstChild->GetLeafPaneCount() + _secondChild->GetLeafPaneCount());
}
// Method Description:
// - This is a helper to determine which direction an "Automatic" split should
// happen in for a given pane, but without using the ActualWidth() and
// ActualHeight() methods. This is used during the initialization of the
// Terminal, when we could be processing many "split-pane" commands _before_
// we've ever laid out the Terminal for the first time. When this happens, the
// Pane's don't have an actual size yet. However, we'd still like to figure
// out how to do an "auto" split when these Panes are all laid out.
// - This method assumes that the Pane we're attempting to split is `target`,
// and this method should be called on the root of a tree of Panes.
// - We'll walk down the tree attempting to find `target`. As we traverse the
// tree, we'll reduce the size passed to each subsequent recursive call. The
// size passed to this method represents how much space this Pane _will_ have
// to use.
// * If this pane is a leaf, and it's the pane we're looking for, use the
// available space to calculate which direction to split in.
// * If this pane is _any other leaf_, then just return nullopt, to indicate
// that the `target` Pane is not down this branch.
// * If this pane is a parent, calculate how much space our children will be
// able to use, and recurse into them.
// Arguments:
// - target: The Pane we're attempting to split.
// - availableSpace: The theoretical space that's available for this pane to be able to split.
// Return Value:
// - nullopt if `target` is not this pane or a child of this pane, otherwise the
// SplitState that `target` would use for an `Automatic` split given
// `availableSpace`
std::optional<winrt::TerminalApp::SplitState> Pane::PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
const winrt::Windows::Foundation::Size availableSpace) const
{
if (_IsLeaf())
{
if (target.get() == this)
{
//If this pane is a leaf, and it's the pane we're looking for, use
//the available space to calculate which direction to split in.
return availableSpace.Width > availableSpace.Height ? SplitState::Vertical : SplitState::Horizontal;
}
else
{
// If this pane is _any other leaf_, then just return nullopt, to
// indicate that the `target` Pane is not down this branch.
return std::nullopt;
}
}
else
{
// If this pane is a parent, calculate how much space our children will
// be able to use, and recurse into them.
const bool isVerticalSplit = _splitState == SplitState::Vertical;
const float firstWidth = isVerticalSplit ? (availableSpace.Width * _desiredSplitPosition) : availableSpace.Width;
const float secondWidth = isVerticalSplit ? (availableSpace.Width - firstWidth) : availableSpace.Width;
const float firstHeight = !isVerticalSplit ? (availableSpace.Height * _desiredSplitPosition) : availableSpace.Height;
const float secondHeight = !isVerticalSplit ? (availableSpace.Height - firstHeight) : availableSpace.Height;
const auto firstResult = _firstChild->PreCalculateAutoSplit(target, { firstWidth, firstHeight });
return firstResult.has_value() ? firstResult : _secondChild->PreCalculateAutoSplit(target, { secondWidth, secondHeight });
}
// We should not possibly be getting here - both the above branches should
// return a value.
FAIL_FAST();
}
DEFINE_EVENT(Pane, GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);

View File

@@ -63,13 +63,10 @@ public:
const GUID& profile,
const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
std::optional<winrt::TerminalApp::SplitState> PreCalculateAutoSplit(const std::shared_ptr<Pane> target, const winrt::Windows::Foundation::Size parentSize) const;
void Shutdown();
void Close();
int GetLeafPaneCount() const noexcept;
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
DECLARE_EVENT(GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
@@ -110,7 +107,8 @@ private:
const GUID& profile,
const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void _CreateRowColDefinitions();
void _CreateRowColDefinitions(const winrt::Windows::Foundation::Size& rootSize);
void _CreateSplitContent();
void _ApplySplitDefinitions();
void _UpdateBorders();
@@ -135,9 +133,6 @@ private:
float _ClampSplitPosition(const bool widthOrHeight, const float requestedValue, const float totalSize) const;
winrt::TerminalApp::SplitState _convertAutomaticSplitState(const winrt::TerminalApp::SplitState& splitType) const;
std::optional<winrt::TerminalApp::SplitState> _preCalculateAutoSplit(const std::shared_ptr<Pane> target, const winrt::Windows::Foundation::Size parentSize) const;
// Function Description:
// - Returns true if the given direction can be used with the given split
// type.
@@ -187,7 +182,7 @@ private:
};
// Helper structure that builds a (roughly) binary tree corresponding
// to the pane tree. Used for laying out panes with snapped sizes.
// to the pane tree. Used for layouting panes with snapped sizes.
struct LayoutSizeNode
{
float size;

View File

@@ -53,7 +53,7 @@ namespace
Dotnet = 1 << 2, // installed as a dotnet global tool
Traditional = 1 << 3, // installed in traditional Program Files locations
// native architecture (choose one)
// native architecutre (choose one)
WOWARM = 1 << 4, // non-native (Windows-on-Windows, ARM variety)
WOWx86 = 1 << 5, // non-native (Windows-on-Windows, x86 variety)

View File

@@ -162,6 +162,40 @@ Temporarily using the Windows Terminal default settings.
<data name="ReloadJsonParseErrorTitle" xml:space="preserve">
<value>Failed to reload settings</value>
</data>
<data name="AboutTitleText" xml:space="preserve">
<value>About</value>
</data>
<data name="VersionLabelText" xml:space="preserve">
<value>Version:</value>
</data>
<data name="DocumentationLabelText" xml:space="preserve">
<value>Documentation
</value>
</data>
<data name="GettingStartedLabelText" xml:space="preserve">
<value>Getting Started
</value>
</data>
<data name="ReleaseNotesLabelText" xml:space="preserve">
<value>Release Notes
</value>
</data>
<data name="PrivacyPolicyLabelText" xml:space="preserve">
<value>Privacy Policy
</value>
</data>
<data name="DocumentationUriValue" xml:space="preserve">
<value>https://aka.ms/terminal-documentation</value>
</data>
<data name="GettingStartedUriValue" xml:space="preserve">
<value>https://aka.ms/terminal-getting-started</value>
</data>
<data name="ReleaseNotesUriValue" xml:space="preserve">
<value>https://aka.ms/terminal-release-notes</value>
</data>
<data name="PrivacyPolicyUriValue" xml:space="preserve">
<value>https://aka.ms/terminal-privacy-policy</value>
</data>
<data name="FeedbackUriValue" xml:space="preserve">
<value>https://aka.ms/terminal-feedback</value>
</data>
@@ -174,6 +208,15 @@ Temporarily using the Windows Terminal default settings.
<data name="SettingsMenuItem" xml:space="preserve">
<value>Settings</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="CloseAll" xml:space="preserve">
<value>Close all</value>
</data>
<data name="CloseWindowWarningTitle" xml:space="preserve">
<value>Do you want to close all tabs?</value>
</data>
<data name="InvalidBackgroundImage" xml:space="preserve">
<value>Found a profile with an invalid "backgroundImage". Defaulting that profile to have no background image. Make sure that when setting a "backgroundImage", the value is a valid file path to an image.</value>
</data>
@@ -240,47 +283,4 @@ Temporarily using the Windows Terminal default settings.
<data name="WindowMinimizeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Minimize</value>
</data>
<data name="AboutDialog.Title" xml:space="preserve">
<value>About</value>
</data>
<data name="AboutDialog.CloseButtonText" xml:space="preserve">
<value>OK</value>
</data>
<data name="AboutDialog_VersionLabel.Text" xml:space="preserve">
<value>Version:</value>
<comment>This is the heading for a version number label</comment>
</data>
<data name="AboutDialog_GettingStartedLink.Content" xml:space="preserve">
<value>Getting Started</value>
<comment>A hyperlink name for a guide on how to get started using Terminal</comment>
</data>
<data name="AboutDialog_DocumentationLink.Content" xml:space="preserve">
<value>Documentation</value>
<comment>A hyperlink name for user documentation</comment>
</data>
<data name="AboutDialog_ReleaseNotesLink.Content" xml:space="preserve">
<value>Release Notes</value>
<comment>A hyperlink name for the Terminal's release notes</comment>
</data>
<data name="AboutDialog_PrivacyPolicyLink.Content" xml:space="preserve">
<value>Privacy Policy</value>
<comment>A hyperlink name for the Terminal's privacy policy</comment>
</data>
<data name="AboutDialog_DisplayNameUnpackaged" xml:space="preserve">
<value>Windows Terminal (Unpackaged)</value>
<comment>This display name is used when the application's name cannot be determined</comment>
</data>
<data name="AboutDialog_VersionUnknown" xml:space="preserve">
<value>Unknown</value>
<comment>This is displayed when the version of the application cannot be determined</comment>
</data>
<data name="CloseAllDialog.CloseButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="CloseAllDialog.PrimaryButtonText" xml:space="preserve">
<value>Close all</value>
</data>
<data name="CloseAllDialog.Title" xml:space="preserve">
<value>Do you want to close all tabs?</value>
</data>
</root>
</root>

View File

@@ -277,12 +277,6 @@ namespace winrt::TerminalApp::implementation
// gains focus, we'll mark it as the new active pane.
_AttachEventHandlersToPane(first);
_AttachEventHandlersToPane(second);
// Immediately update our tracker of the focused pane now. If we're
// splitting panes during startup (from a commandline), then it's
// possible that the focus events won't propagate immediately. Updating
// the focus here will give the same effect though.
_UpdateActivePane(second);
}
// Method Description:
@@ -393,28 +387,6 @@ namespace winrt::TerminalApp::implementation
});
}
// Method Description:
// - Mark the given pane as the active pane in this tab. All other panes
// will be marked as inactive. We'll also update our own UI state to
// reflect this newly active pane.
// Arguments:
// - pane: a Pane to mark as active.
// Return Value:
// - <none>
void Tab::_UpdateActivePane(std::shared_ptr<Pane> pane)
{
// Clear the active state of the entire tree, and mark only the pane as active.
_rootPane->ClearActive();
_activePane = pane;
_activePane->SetActive();
// Update our own title text to match the newly-active pane.
SetTabText(GetActiveTitle());
// Raise our own ActivePaneChanged event.
_ActivePaneChangedHandlers();
}
// Method Description:
// - Add an event handler to this pane's GotFocus event. When that pane gains
// focus, we'll mark it as the new active pane. We'll also query the title of
@@ -434,37 +406,19 @@ namespace winrt::TerminalApp::implementation
if (tab && sender != tab->_activePane)
{
tab->_UpdateActivePane(sender);
// Clear the active state of the entire tree, and mark only the sender as active.
tab->_rootPane->ClearActive();
tab->_activePane = sender;
tab->_activePane->SetActive();
// Update our own title text to match the newly-active pane.
tab->SetTabText(tab->GetActiveTitle());
// Raise our own ActivePaneChanged event.
tab->_ActivePaneChangedHandlers();
}
});
}
// Method Description:
// - Get the total number of leaf panes in this tab. This will be the number
// of actual controls hosted by this tab.
// Arguments:
// - <none>
// Return Value:
// - The total number of leaf panes hosted by this tab.
int Tab::_GetLeafPaneCount() const noexcept
{
return _rootPane->GetLeafPaneCount();
}
// Method Description:
// - This is a helper to determine which direction an "Automatic" split should
// happen in for the active pane of this tab, but without using the ActualWidth() and
// ActualHeight() methods.
// - See Pane::PreCalculateAutoSplit
// Arguments:
// - availableSpace: The theoretical space that's available for this Tab's content
// Return Value:
// - The SplitState that we should use for an `Automatic` split given
// `availableSpace`
SplitState Tab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const
{
return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitState::Vertical);
}
DEFINE_EVENT(Tab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
}

View File

@@ -5,12 +5,6 @@
#include "Pane.h"
#include "Tab.g.h"
// fwdecl unittest classes
namespace TerminalAppLocalTests
{
class TabTests;
};
namespace winrt::TerminalApp::implementation
{
struct Tab : public TabT<Tab>
@@ -38,7 +32,6 @@ namespace winrt::TerminalApp::implementation
winrt::fire_and_forget UpdateIcon(const winrt::hstring iconPath);
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
SplitState PreCalculateAutoSplit(winrt::Windows::Foundation::Size rootSize) const;
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
void ResizePane(const winrt::TerminalApp::Direction& direction);
@@ -71,10 +64,5 @@ namespace winrt::TerminalApp::implementation
void _AttachEventHandlersToControl(const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void _AttachEventHandlersToPane(std::shared_ptr<Pane> pane);
int _GetLeafPaneCount() const noexcept;
void _UpdateActivePane(std::shared_ptr<Pane> pane);
friend class ::TerminalAppLocalTests::TabTests;
};
}

View File

@@ -15,7 +15,6 @@
#include "AzureCloudShellGenerator.h" // For AzureConnectionType
#include "TelnetGenerator.h" // For TelnetConnectionType
#include "TabRowControl.h"
#include "DebugTapConnection.h"
using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
@@ -64,20 +63,11 @@ namespace winrt::TerminalApp::implementation
_tabView = _tabRow.TabView();
_rearranging = false;
// GH#2455 - Make sure to try/catch calls to Application::Current,
// because that _won't_ be an instance of TerminalApp::App in the
// LocalTests
auto isElevated = false;
try
{
// GH#3581 - There's a platform limitation that causes us to crash when we rearrange tabs.
// Xaml tries to send a drag visual (to wit: a screenshot) to the drag hosting process,
// but that process is running at a different IL than us.
// For now, we're disabling elevated drag.
isElevated = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
}
CATCH_LOG();
// GH#3581 - There's a platform limitation that causes us to crash when we rearrange tabs.
// Xaml tries to send a drag visual (to wit: a screenshot) to the drag hosting process,
// but that process is running at a different IL than us.
// For now, we're disabling elevated drag.
const auto isElevated = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsElevated();
_tabView.CanReorderTabs(!isElevated);
_tabView.CanDragTabs(!isElevated);
@@ -147,115 +137,161 @@ namespace winrt::TerminalApp::implementation
_tabContent.SizeChanged({ this, &TerminalPage::_OnContentSizeChanged });
// Once the page is actually laid out on the screen, trigger all our
// startup actions. Things like Panes need to know at least how big the
// window will be, so they can subdivide that space.
//
// _OnFirstLayout will remove this handler so it doesn't get called more than once.
_layoutUpdatedRevoker = _tabContent.LayoutUpdated(winrt::auto_revoke, { this, &TerminalPage::_OnFirstLayout });
}
// Method Description:
// - This method is called once on startup, on the first LayoutUpdated event.
// We'll use this event to know that we have an ActualWidth and
// ActualHeight, so we can now attempt to process our list of startup
// actions.
// - We'll remove this event handler when the event is first handled.
// - If there are no startup actions, we'll open a single tab with the
// default profile.
// Arguments:
// - <unused>
// Return Value:
// - <none>
void TerminalPage::_OnFirstLayout(const IInspectable& /*sender*/, const IInspectable& /*eventArgs*/)
{
// Only let this succeed once.
_layoutUpdatedRevoker.revoke();
// This event fires every time the layout changes, but it is always the
// last one to fire in any layout change chain. That gives us great
// flexibility in finding the right point at which to initialize our
// renderer (and our terminal). Any earlier than the last layout update
// and we may not know the terminal's starting size.
if (_startupState == StartupState::NotInitialized)
// Actually start the terminal.
if (_appArgs.GetStartupActions().empty())
{
_OpenNewTab(nullptr);
}
else
{
_startupState = StartupState::InStartup;
_appArgs.ValidateStartupCommands();
if (_appArgs.GetStartupActions().empty())
{
_OpenNewTab(nullptr);
_startupState = StartupState::Initialized;
_InitializedHandlers(*this, nullptr);
}
else
{
_ProcessStartupActions();
}
// This will kick off a chain of events to perform each startup
// action. As each startup action is completed, the next will be
// fired.
_ProcessNextStartupAction();
}
}
// Method Description:
// - Process all the startup actions in our list of startup actions. We'll
// do this all at once here.
// - Process the next startup action in our list of startup actions. When
// that action is complete, fire the next (if there are any more).
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget TerminalPage::_ProcessStartupActions()
fire_and_forget TerminalPage::_ProcessNextStartupAction()
{
// If there are no actions left, do nothing.
if (_appArgs.GetStartupActions().empty())
{
return;
}
// Get the next action to be processed
auto nextAction = _appArgs.GetStartupActions().front();
_appArgs.GetStartupActions().pop_front();
auto weakThis{ get_weak() };
// Handle it on a subsequent pass of the UI thread.
co_await winrt::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal);
// Handle it on the UI thread.
co_await winrt::resume_foreground(Dispatcher(), CoreDispatcherPriority::Low);
if (auto page{ weakThis.get() })
{
for (const auto& action : _appArgs.GetStartupActions())
{
_actionDispatch->DoAction(action);
}
_startupState = StartupState::Initialized;
_InitializedHandlers(*this, nullptr);
page->_actionDispatch->DoAction(nextAction);
// Kick off the next action to be handled (if necessary)
page->_ProcessNextStartupAction();
}
}
// Method Description:
// - Show a ContentDialog with a single "Ok" button to dismiss. Looks up the
// the title and text from our Resources using the provided keys.
// - Only one dialog can be visible at a time. If another dialog is visible
// when this is called, nothing happens. See _ShowDialog for details
// Arguments:
// - titleKey: The key to use to lookup the title text from our resources.
// - contentKey: The key to use to lookup the content text from our resources.
void TerminalPage::ShowOkDialog(const winrt::hstring& titleKey,
const winrt::hstring& contentKey)
{
auto title = GetLibraryResourceString(titleKey);
auto message = GetLibraryResourceString(contentKey);
auto buttonText = RS_(L"Ok");
WUX::Controls::ContentDialog dialog;
dialog.Title(winrt::box_value(title));
dialog.Content(winrt::box_value(message));
dialog.CloseButtonText(buttonText);
dialog.DefaultButton(WUX::Controls::ContentDialogButton::Close);
_showDialogHandlers(*this, dialog);
}
// Method Description:
// - Show a dialog with "About" information. Displays the app's Display
// Name, version, getting started link, documentation link, release
// Notes link, and privacy policy link.
void TerminalPage::_ShowAboutDialog()
{
_showDialogHandlers(*this, FindName(L"AboutDialog").try_as<WUX::Controls::ContentDialog>());
}
const auto title = RS_(L"AboutTitleText");
const auto versionLabel = RS_(L"VersionLabelText");
const auto gettingStartedLabel = RS_(L"GettingStartedLabelText");
const auto documentationLabel = RS_(L"DocumentationLabelText");
const auto releaseNotesLabel = RS_(L"ReleaseNotesLabelText");
const auto privacyPolicyLabel = RS_(L"PrivacyPolicyLabelText");
const auto gettingStartedUriValue = RS_(L"GettingStartedUriValue");
const auto documentationUriValue = RS_(L"DocumentationUriValue");
const auto releaseNotesUriValue = RS_(L"ReleaseNotesUriValue");
const auto privacyPolicyUriValue = RS_(L"PrivacyPolicyUriValue");
const auto package = winrt::Windows::ApplicationModel::Package::Current();
const auto packageName = package.DisplayName();
const auto version = package.Id().Version();
winrt::Windows::UI::Xaml::Documents::Run about;
winrt::Windows::UI::Xaml::Documents::Run gettingStarted;
winrt::Windows::UI::Xaml::Documents::Run documentation;
winrt::Windows::UI::Xaml::Documents::Run releaseNotes;
winrt::Windows::UI::Xaml::Documents::Run privacyPolicy;
winrt::Windows::UI::Xaml::Documents::Hyperlink gettingStartedLink;
winrt::Windows::UI::Xaml::Documents::Hyperlink documentationLink;
winrt::Windows::UI::Xaml::Documents::Hyperlink releaseNotesLink;
winrt::Windows::UI::Xaml::Documents::Hyperlink privacyPolicyLink;
std::wstringstream aboutTextStream;
winrt::hstring TerminalPage::ApplicationDisplayName()
{
try
{
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
return package.DisplayName();
}
CATCH_LOG();
gettingStarted.Text(gettingStartedLabel);
documentation.Text(documentationLabel);
releaseNotes.Text(releaseNotesLabel);
privacyPolicy.Text(privacyPolicyLabel);
return RS_(L"AboutDialog_DisplayNameUnpackaged");
}
winrt::Windows::Foundation::Uri gettingStartedUri{ gettingStartedUriValue };
winrt::Windows::Foundation::Uri documentationUri{ documentationUriValue };
winrt::Windows::Foundation::Uri releaseNotesUri{ releaseNotesUriValue };
winrt::Windows::Foundation::Uri privacyPolicyUri{ privacyPolicyUriValue };
winrt::hstring TerminalPage::ApplicationVersion()
{
try
{
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
const auto version{ package.Id().Version() };
winrt::hstring formatted{ wil::str_printf<std::wstring>(L"%u.%u.%u.%u", version.Major, version.Minor, version.Build, version.Revision) };
return formatted;
}
CATCH_LOG();
gettingStartedLink.NavigateUri(gettingStartedUri);
documentationLink.NavigateUri(documentationUri);
releaseNotesLink.NavigateUri(releaseNotesUri);
privacyPolicyLink.NavigateUri(privacyPolicyUri);
return RS_(L"AboutDialog_VersionUnknown");
gettingStartedLink.Inlines().Append(gettingStarted);
documentationLink.Inlines().Append(documentation);
releaseNotesLink.Inlines().Append(releaseNotes);
privacyPolicyLink.Inlines().Append(privacyPolicy);
// Format our about text. It will look like the following:
// <Display Name>
// Version: <Major>.<Minor>.<Build>.<Revision>
// Getting Started
// Documentation
// Release Notes
// Privacy Policy
aboutTextStream << packageName.c_str() << L"\n";
aboutTextStream << versionLabel.c_str() << L" ";
aboutTextStream << version.Major << L"." << version.Minor << L"." << version.Build << L"." << version.Revision << L"\n";
winrt::hstring aboutText{ aboutTextStream.str() };
about.Text(aboutText);
const auto buttonText = RS_(L"Ok");
WUX::Controls::TextBlock aboutTextBlock;
aboutTextBlock.Inlines().Append(about);
aboutTextBlock.Inlines().Append(gettingStartedLink);
aboutTextBlock.Inlines().Append(documentationLink);
aboutTextBlock.Inlines().Append(releaseNotesLink);
aboutTextBlock.Inlines().Append(privacyPolicyLink);
aboutTextBlock.IsTextSelectionEnabled(true);
WUX::Controls::ContentDialog dialog;
dialog.Title(winrt::box_value(title));
dialog.Content(aboutTextBlock);
dialog.CloseButtonText(buttonText);
dialog.DefaultButton(WUX::Controls::ContentDialogButton::Close);
_showDialogHandlers(*this, dialog);
}
// Method Description:
@@ -267,7 +303,19 @@ namespace winrt::TerminalApp::implementation
// when this is called, nothing happens. See _ShowDialog for details
void TerminalPage::_ShowCloseWarningDialog()
{
_showDialogHandlers(*this, FindName(L"CloseAllDialog").try_as<WUX::Controls::ContentDialog>());
auto title = RS_(L"CloseWindowWarningTitle");
auto primaryButtonText = RS_(L"CloseAll");
auto closeButtonText = RS_(L"Cancel");
WUX::Controls::ContentDialog dialog;
dialog.Title(winrt::box_value(title));
dialog.CloseButtonText(closeButtonText);
dialog.PrimaryButtonText(primaryButtonText);
dialog.DefaultButton(WUX::Controls::ContentDialogButton::Primary);
auto token = dialog.PrimaryButtonClick({ this, &TerminalPage::_CloseWarningPrimaryButtonOnClick });
_showDialogHandlers(*this, dialog);
}
// Method Description:
@@ -358,15 +406,7 @@ namespace winrt::TerminalApp::implementation
// add static items
{
// GH#2455 - Make sure to try/catch calls to Application::Current,
// because that _won't_ be an instance of TerminalApp::App in the
// LocalTests
auto isUwp = false;
try
{
isUwp = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
}
CATCH_LOG();
const auto isUwp = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
if (!isUwp)
{
@@ -436,7 +476,6 @@ namespace winrt::TerminalApp::implementation
// control which profile is created and with possible other
// configurations. See CascadiaSettings::BuildSettings for more details.
void TerminalPage::_OpenNewTab(const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs)
try
{
const auto [profileGuid, settings] = _settings->BuildSettings(newTerminalArgs);
@@ -460,7 +499,6 @@ namespace winrt::TerminalApp::implementation
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
}
CATCH_LOG();
winrt::fire_and_forget TerminalPage::_RemoveOnCloseRoutine(Microsoft::UI::Xaml::Controls::TabViewItem tabViewItem, winrt::com_ptr<TerminalPage> page)
{
@@ -476,25 +514,11 @@ namespace winrt::TerminalApp::implementation
// - settings: the TerminalSettings object to use to create the TerminalControl with.
void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings)
{
const bool isFirstTab = _tabs.Size() == 0;
// Initialize the new tab
// Create a connection based on the values in our settings object.
auto connection = _CreateConnectionFromSettings(profileGuid, settings);
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);
}
}
const auto connection = _CreateConnectionFromSettings(profileGuid, settings);
TermControl term{ settings, connection };
// Add the new tab to the list of our tabs.
@@ -544,17 +568,20 @@ namespace winrt::TerminalApp::implementation
}
});
if (debugConnection) // this will only be set if global debugging is on and tap is active
// If this is the first tab, we don't need to kick off the event to get
// the tab's content added to the root of the page. just do it
// immediately.
if (isFirstTab)
{
TermControl newControl{ settings, debugConnection };
_RegisterTerminalEvents(newControl, *newTabImpl);
// Split (auto) with the debug tap.
newTabImpl->SplitPane(SplitState::Automatic, profileGuid, newControl);
_tabContent.Children().Clear();
_tabContent.Children().Append(newTabImpl->GetRootElement());
}
else
{
// This kicks off TabView::SelectionChanged, in response to which
// we'll attach the terminal's Xaml control to the Xaml root.
_tabView.SelectedItem(tabViewItem);
}
// 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:
@@ -783,30 +810,13 @@ namespace winrt::TerminalApp::implementation
{
if (auto index{ _GetFocusedTabIndex() })
{
try
auto focusedTab = _GetStrongTabImpl(*index);
const auto& profileGuid = focusedTab->GetFocusedProfile();
if (profileGuid.has_value())
{
auto focusedTab = _GetStrongTabImpl(*index);
// 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.
//
// 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.
const auto& profileGuid = focusedTab->GetFocusedProfile();
if (profileGuid.has_value())
{
const auto settings = _settings->BuildSettings(profileGuid.value());
_CreateNewTabFromSettings(profileGuid.value(), settings);
}
const auto settings = _settings->BuildSettings(profileGuid.value());
_CreateNewTabFromSettings(profileGuid.value(), settings);
}
CATCH_LOG();
}
}
@@ -891,36 +901,20 @@ namespace winrt::TerminalApp::implementation
// Wraparound math. By adding tabCount and then calculating modulo tabCount,
// we clamp the values to the range [0, tabCount) while still supporting moving
// leftward from 0 to tabCount - 1.
const auto newTabIndex = ((tabCount + *index + (bMoveRight ? 1 : -1)) % tabCount);
_SelectTab(newTabIndex);
_SetFocusedTabIndex(((tabCount + *index + (bMoveRight ? 1 : -1)) % tabCount));
}
}
// Method Description:
// - Sets focus to the desired tab. Returns false if the provided tabIndex
// is greater than the number of tabs we have.
// - During startup, we'll immediately set the selected tab as focused.
// - After startup, we'll dispatch an async method to set the the selected
// item of the TabView, which will then also trigger a
// TabView::SelectionChanged, handled in
// TerminalPage::_OnTabSelectionChanged
// Return Value:
// true iff we were able to select that tab index, false otherwise
bool TerminalPage::_SelectTab(const uint32_t tabIndex)
{
if (tabIndex >= 0 && tabIndex < _tabs.Size())
{
if (_startupState == StartupState::InStartup)
{
auto tab{ _GetStrongTabImpl(tabIndex) };
_tabView.SelectedItem(tab->GetTabViewItem());
_UpdatedSelectedTab(tabIndex);
}
else
{
_SetFocusedTabIndex(tabIndex);
}
_SetFocusedTabIndex(tabIndex);
return true;
}
return false;
@@ -973,16 +967,6 @@ namespace winrt::TerminalApp::implementation
return std::nullopt;
}
// Method Description:
// - An async method for changing the focused tab on the UI thread. This
// method will _only_ set the selected item of the TabView, which will
// then also trigger a TabView::SelectionChanged event, which we'll handle
// in TerminalPage::_OnTabSelectionChanged, where we'll mark the new tab
// as focused.
// Arguments:
// - tabIndex: the index in the list of tabs to focus.
// Return Value:
// - <none>
winrt::fire_and_forget TerminalPage::_SetFocusedTabIndex(const uint32_t tabIndex)
{
// GH#1117: This is a workaround because _tabView.SelectedIndex(tabIndex)
@@ -1091,65 +1075,42 @@ namespace winrt::TerminalApp::implementation
return;
}
try
auto focusedTab = _GetStrongTabImpl(*indexOpt);
winrt::Microsoft::Terminal::Settings::TerminalSettings controlSettings;
GUID realGuid;
bool profileFound = false;
if (splitMode == TerminalApp::SplitType::Duplicate)
{
auto focusedTab = _GetStrongTabImpl(*indexOpt);
winrt::Microsoft::Terminal::Settings::TerminalSettings controlSettings;
GUID realGuid;
bool profileFound = false;
if (splitMode == TerminalApp::SplitType::Duplicate)
std::optional<GUID> current_guid = focusedTab->GetFocusedProfile();
if (current_guid)
{
std::optional<GUID> current_guid = focusedTab->GetFocusedProfile();
if (current_guid)
{
profileFound = true;
controlSettings = _settings->BuildSettings(current_guid.value());
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
// settings so we can duplicate this tab/pane.
//
// 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.
profileFound = true;
controlSettings = _settings->BuildSettings(current_guid.value());
realGuid = current_guid.value();
}
if (!profileFound)
{
std::tie(realGuid, controlSettings) = _settings->BuildSettings(newTerminalArgs);
}
const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings);
const auto canSplit = focusedTab->CanSplitPane(splitType);
if (!canSplit && _startupState == StartupState::Initialized)
{
return;
}
auto realSplitType = splitType;
if (realSplitType == SplitState::Automatic && _startupState < StartupState::Initialized)
{
float contentWidth = gsl::narrow_cast<float>(_tabContent.ActualWidth());
float contentHeight = gsl::narrow_cast<float>(_tabContent.ActualHeight());
realSplitType = focusedTab->PreCalculateAutoSplit({ contentWidth, contentHeight });
}
TermControl newControl{ controlSettings, controlConnection };
// Hookup our event handlers to the new terminal
_RegisterTerminalEvents(newControl, *focusedTab);
focusedTab->SplitPane(realSplitType, realGuid, newControl);
}
CATCH_LOG();
if (!profileFound)
{
std::tie(realGuid, controlSettings) = _settings->BuildSettings(newTerminalArgs);
}
const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings);
const auto canSplit = focusedTab->CanSplitPane(splitType);
if (!canSplit)
{
return;
}
TermControl newControl{ controlSettings, controlConnection };
// Hookup our event handlers to the new terminal
_RegisterTerminalEvents(newControl, *focusedTab);
focusedTab->SplitPane(splitType, realGuid, newControl);
}
// Method Description:
@@ -1465,31 +1426,6 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_UpdatedSelectedTab(const int32_t index)
{
// Unfocus all the tabs.
for (auto tab : _tabs)
{
auto tabImpl{ _GetStrongTabImpl(tab) };
tabImpl->SetFocused(false);
}
if (index >= 0)
{
try
{
auto tab{ _GetStrongTabImpl(index) };
_tabContent.Children().Clear();
_tabContent.Children().Append(tab->GetRootElement());
tab->SetFocused(true);
_titleChangeHandlers(*this, Title());
}
CATCH_LOG();
}
}
// Method Description:
// - Responds to the TabView control's Selection Changed event (to move a
// new terminal control into focus) when not in in the middle of a tab rearrangement.
@@ -1502,7 +1438,28 @@ namespace winrt::TerminalApp::implementation
{
auto tabView = sender.as<MUX::Controls::TabView>();
auto selectedIndex = tabView.SelectedIndex();
_UpdatedSelectedTab(selectedIndex);
// Unfocus all the tabs.
for (auto tab : _tabs)
{
auto tabImpl{ _GetStrongTabImpl(tab) };
tabImpl->SetFocused(false);
}
if (selectedIndex >= 0)
{
try
{
auto tab{ _GetStrongTabImpl(selectedIndex) };
_tabContent.Children().Clear();
_tabContent.Children().Append(tab->GetRootElement());
tab->SetFocused(true);
_titleChangeHandlers(*this, Title());
}
CATCH_LOG();
}
}
}
@@ -1568,28 +1525,16 @@ namespace winrt::TerminalApp::implementation
for (auto& profile : profiles)
{
const GUID profileGuid = profile.GetGuid();
const auto settings = _settings->BuildSettings(profileGuid);
try
for (auto tab : _tabs)
{
// BuildSettings can throw an exception if the profileGuid does
// not belong to an actual profile in the list of profiles.
const auto settings = _settings->BuildSettings(profileGuid);
for (auto tab : _tabs)
{
// Attempt to reload the settings of any panes with this profile
auto tabImpl{ _GetStrongTabImpl(tab) };
tabImpl->UpdateSettings(settings, profileGuid);
}
// Attempt to reload the settings of any panes with this profile
auto tabImpl{ _GetStrongTabImpl(tab) };
tabImpl->UpdateSettings(settings, profileGuid);
}
CATCH_LOG();
}
// 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.
for (auto tab : _tabs)
{
@@ -1633,7 +1578,7 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Attempts to parse an array of commandline args into a list of
// commands to execute, and then parses these commands. As commands are
// successfully parsed, they will generate ShortcutActions for us to be
// succesfully parsed, they will generate ShortcutActions for us to be
// able to execute. If we fail to parse any commands, we'll return the
// error code from the failure to parse that command, and stop processing
// additional commands.
@@ -1731,7 +1676,7 @@ namespace winrt::TerminalApp::implementation
// Arguments:
// - <none>
// Return Value:
// - the help text or error message for the provided commandline, if one
// - the help text or error message for the providied commandline, if one
// exists, otherwise the empty string.
winrt::hstring TerminalPage::EarlyExitMessage()
{

View File

@@ -20,13 +20,6 @@ namespace TerminalAppLocalTests
namespace winrt::TerminalApp::implementation
{
enum StartupState : int
{
NotInitialized = 0,
InStartup = 1,
Initialized = 2
};
struct TerminalPage : TerminalPageT<TerminalPage>
{
public:
@@ -38,13 +31,12 @@ namespace winrt::TerminalApp::implementation
hstring Title();
void ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey);
void TitlebarClicked();
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
winrt::hstring ApplicationDisplayName();
winrt::hstring ApplicationVersion();
void CloseWindow();
int32_t SetStartupCommandline(winrt::array_view<const hstring> args);
@@ -56,11 +48,8 @@ namespace winrt::TerminalApp::implementation
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(ShowDialog, _showDialogHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::Controls::ContentDialog);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(ToggleFullscreen, _toggleFullscreenHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::ToggleFullscreenEventArgs);
TYPED_EVENT(Initialized, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs);
private:
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
// If you add controls here, but forget to null them either here or in
// the ctor, you're going to have a bad time. It'll mysteriously fail to
// activate the app.
@@ -86,12 +75,9 @@ namespace winrt::TerminalApp::implementation
winrt::com_ptr<ShortcutActionDispatch> _actionDispatch{ winrt::make_self<ShortcutActionDispatch>() };
winrt::Windows::UI::Xaml::Controls::Grid::LayoutUpdated_revoker _layoutUpdatedRevoker;
StartupState _startupState{ StartupState::NotInitialized };
::TerminalApp::AppCommandlineArgs _appArgs;
int _ParseArgs(winrt::array_view<const hstring>& args);
winrt::fire_and_forget _ProcessStartupActions();
fire_and_forget _ProcessNextStartupAction();
void _ShowAboutDialog();
void _ShowCloseWarningDialog();
@@ -155,8 +141,6 @@ namespace winrt::TerminalApp::implementation
void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs);
void _OnContentSizeChanged(const IInspectable& /*sender*/, Windows::UI::Xaml::SizeChangedEventArgs const& e);
void _OnTabCloseRequested(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabCloseRequestedEventArgs& eventArgs);
void _OnFirstLayout(const IInspectable& sender, const IInspectable& eventArgs);
void _UpdatedSelectedTab(const int32_t index);
void _Find();

View File

@@ -13,15 +13,10 @@ namespace TerminalApp
Int32 SetStartupCommandline(String[] commands);
String EarlyExitMessage { get; };
// XAML bound properties
String ApplicationDisplayName { get; };
String ApplicationVersion { get; };
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.Controls.ContentDialog> ShowDialog;
event Windows.Foundation.TypedEventHandler<Object, ToggleFullscreenEventArgs> ToggleFullscreen;
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.RoutedEventArgs> Initialized;
}
}

View File

@@ -19,39 +19,5 @@ the MIT License. See LICENSE in the project root for license information. -->
<local:TabRowControl x:Name="TabRow" Grid.Row="0" />
<Grid x:Name="TabContent" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
<ContentDialog
x:Load="False"
x:Name="AboutDialog"
x:Uid="AboutDialog"
DefaultButton="Close">
<StackPanel Orientation="Vertical">
<TextBlock IsTextSelectionEnabled="True">
<Run Text="{x:Bind ApplicationDisplayName}" /> <LineBreak />
<Run x:Uid="AboutDialog_VersionLabel" />
<Run Text="{x:Bind ApplicationVersion}" />
</TextBlock>
<HyperlinkButton
x:Uid="AboutDialog_GettingStartedLink"
NavigateUri="https://aka.ms/terminal-getting-started" />
<HyperlinkButton
x:Uid="AboutDialog_DocumentationLink"
NavigateUri="https://aka.ms/terminal-documentation" />
<HyperlinkButton
x:Uid="AboutDialog_ReleaseNotesLink"
NavigateUri="https://aka.ms/terminal-release-notes" />
<HyperlinkButton
x:Uid="AboutDialog_PrivacyPolicyLink"
NavigateUri="https://aka.ms/terminal-privacy-policy" />
</StackPanel>
</ContentDialog>
<ContentDialog
x:Load="False"
x:Name="CloseAllDialog"
x:Uid="CloseAllDialog"
DefaultButton="Primary"
PrimaryButtonClick="_CloseWarningPrimaryButtonOnClick">
</ContentDialog>
</Grid>
</Page>

View File

@@ -22,7 +22,6 @@
"colorScheme": "Vintage",
"cursorColor": "#FFFFFF",
"cursorShape": "bar",
"fontFace": "Cascadia Code",
"icon": "ms-appx:///ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.png",
"padding": "8, 8, 8, 8",
"snapOnInput": true,

View File

@@ -23,7 +23,7 @@
"closeOnExit": "graceful",
"colorScheme": "Campbell Powershell",
"cursorShape": "bar",
"fontFace": "Cascadia Code",
"fontFace": "Consolas",
"fontSize": 12,
"historySize": 9001,
"icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
@@ -41,7 +41,7 @@
"closeOnExit": "graceful",
"colorScheme": "Campbell",
"cursorShape": "bar",
"fontFace": "Cascadia Code",
"fontFace": "Consolas",
"fontSize": 12,
"historySize": 9001,
"icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png",

View File

@@ -107,7 +107,6 @@
<ClInclude Include="../ActionAndArgs.h">
<DependentUpon>../ActionArgs.idl</DependentUpon>
</ClInclude>
<ClInclude Include="../DebugTapConnection.h" />
<ClInclude Include="../AppKeyBindings.h">
<DependentUpon>../AppKeyBindings.idl</DependentUpon>
</ClInclude>
@@ -154,7 +153,6 @@
<ClCompile Include="../WslDistroGenerator.cpp" />
<ClCompile Include="../AzureCloudShellGenerator.cpp" />
<ClCompile Include="../Pane.LayoutSizeNode.cpp" />
<ClCompile Include="../DebugTapConnection.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
@@ -218,7 +216,7 @@
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>
<PRIResource Include="..\Resources\*\Resources.resw" />
<PRIResource Include="../Resources/Resources.language-en.resw" />
<None Include="../packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->

View File

@@ -53,7 +53,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_transitionToState(ConnectionState::Connecting);
// Set initial window title.
// Set initial winodw title.
_TerminalOutputHandlers(L"\x1b]0;Telnet\x7");
}
catch (...)

View File

@@ -59,7 +59,7 @@
<Midl Include="TelnetConnection.idl" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources\*\Resources.resw" />
<PRIResource Include="Resources/Resources.language-en.resw" />
<None Include="packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->

View File

@@ -159,7 +159,7 @@
</data>
<data name="TerminalControl_ControlType" xml:space="preserve">
<value>terminal</value>
<comment>The type of control that the terminal adheres to. Used to identify how a user can interact with this kind of control.</comment>
<comment>The type of control that the terminal ahderes to. Used to identify how a user can interact with this kind of control.</comment>
</data>
<data name="SearchBox_Close.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Close Search Box</value>

View File

@@ -172,13 +172,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
TextBlock().FontSize(fontSizePx);
TextBlock().FontFamily(Media::FontFamily(fontArgs->FontFace()));
const auto canvasActualWidth = Canvas().ActualWidth();
const auto widthToTerminalEnd = canvasActualWidth - ::base::ClampedNumeric<double>(clientCursorPos.X);
// Make sure that we're setting the MaxWidth to a positive number - a
// negative number here will crash us in mysterious ways with a useless
// stack trace
const auto newMaxWidth = std::max<double>(0.0, widthToTerminalEnd);
TextBlock().MaxWidth(newMaxWidth);
const auto widthToTerminalEnd = Canvas().ActualWidth() - ::base::ClampedNumeric<double>(clientCursorPos.X);
TextBlock().MaxWidth(widthToTerminalEnd);
// Set the text block bounds
const auto yOffset = ::base::ClampedNumeric<float>(TextBlock().ActualHeight()) - fontHeight;

View File

@@ -69,37 +69,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_cursorTimer{},
_lastMouseClickTimestamp{},
_lastMouseClickPos{},
_selectionNeedsToBeCopied{ false },
_searchBox{ nullptr }
_searchBox{ nullptr },
_focusRaisedClickPos{ std::nullopt },
_clickDrag{ false }
{
_EnsureStaticInitialization();
InitializeComponent();
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1);
_terminal->SetTitleChangedCallback(pfnTitleChanged);
auto pfnBackgroundColorChanged = std::bind(&TermControl::_BackgroundColorChanged, this, std::placeholders::_1);
_terminal->SetBackgroundCallback(pfnBackgroundColorChanged);
auto pfnScrollPositionChanged = std::bind(&TermControl::_TerminalScrollPositionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
_terminal->SetScrollPositionChangedCallback(pfnScrollPositionChanged);
// This event is explicitly revoked in the destructor: does not need weak_ref
auto onReceiveOutputFn = [this](const hstring str) {
_terminal->Write(str);
};
_connectionOutputEventToken = _connection.TerminalOutput(onReceiveOutputFn);
auto inputFn = std::bind(&TermControl::_SendInputToConnection, this, std::placeholders::_1);
_terminal->SetWriteInputCallback(inputFn);
// Subscribe to the connection's disconnected event and call our connection closed handlers.
_connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
_ConnectionStateChangedHandlers(*this, nullptr);
});
// Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout
_layoutUpdatedRevoker = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
@@ -115,9 +91,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
});
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000));
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
_autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll });
// Subscribe to the connection's disconnected event and call our connection closed handlers.
_connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
_ConnectionStateChangedHandlers(*this, nullptr);
});
_ApplyUISettings();
}
@@ -150,7 +127,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControl::_Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
{
if (text.size() == 0 || _closing)
if (text.size() == 0)
{
return;
}
@@ -207,11 +184,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// If 'weakThis' is locked, then we can safely work with 'this'
if (auto control{ weakThis.get() })
{
if (_closing)
{
return;
}
// Update our control settings
_ApplyUISettings();
@@ -281,7 +253,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Apply settings for scrollbar
if (_settings.ScrollState() == ScrollbarState::Hidden)
{
// In the scenario where the user has turned off the OS setting to automatically hide scrollbars, the
// In the scenario where the user has turned off the OS setting to automatically hide scollbars, the
// Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
// achieve the intended effect.
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
@@ -441,7 +413,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer()
try
{
if (_initializedTerminal && !_closing) // only set up the automation peer if we're ready to go live
if (GetUiaData())
{
// create a custom automation peer with this code pattern:
// (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
@@ -479,14 +451,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return _connection.State();
}
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged()
winrt::fire_and_forget TermControl::SwapChainChanged()
{
{ // lock scope
auto terminalLock = _terminal->LockForReading();
if (!_initializedTerminal)
{
return;
}
if (!_initializedTerminal)
{
return;
}
auto chain = _renderEngine->GetSwapChain();
@@ -494,153 +463,186 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
co_await winrt::resume_foreground(Dispatcher());
// If 'weakThis' is locked, then we can safely work with 'this'
if (auto control{ weakThis.get() })
{
auto terminalLock = _terminal->LockForWriting();
_AttachDxgiSwapChainToXaml(chain.Get());
if (_terminal)
{
auto lock = _terminal->LockForWriting();
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(chain.Get());
}
}
}
void TermControl::_AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain)
winrt::fire_and_forget TermControl::_SwapChainRoutine()
{
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(swapChain);
auto chain = _renderEngine->GetSwapChain();
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
if (auto control{ weakThis.get() })
{
if (_terminal)
{
auto lock = _terminal->LockForWriting();
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(chain.Get());
}
}
}
bool TermControl::_InitializeTerminal()
{
{ // scope for terminalLock
auto terminalLock = _terminal->LockForWriting();
if (_initializedTerminal)
{
return false;
}
if (_initializedTerminal)
{
return false;
}
const auto windowWidth = SwapChainPanel().ActualWidth(); // Width() and Height() are NaN?
const auto windowHeight = SwapChainPanel().ActualHeight();
const auto windowWidth = SwapChainPanel().ActualWidth(); // Width() and Height() are NaN?
const auto windowHeight = SwapChainPanel().ActualHeight();
if (windowWidth == 0 || windowHeight == 0)
{
return false;
}
if (windowWidth == 0 || windowHeight == 0)
{
return false;
}
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
// 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();
// 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;
// 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;
THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
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());
// Set up the DX Engine
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
_renderer->AddRenderEngine(dxEngine.get());
// Initialize our font with the renderer
// We don't have to care about DPI. We'll get a change message immediately if it's not 96
// and react accordingly.
_UpdateFont(true);
// Initialize our font with the renderer
// We don't have to care about DPI. We'll get a change message immediately if it's not 96
// and react accordingly.
_UpdateFont(true);
const COORD windowSize{ static_cast<short>(windowWidth), static_cast<short>(windowHeight) };
const COORD windowSize{ static_cast<short>(windowWidth), static_cast<short>(windowHeight) };
// Fist set up the dx engine with the window size in pixels.
// Then, using the font, get the number of characters that can fit.
// Resize our terminal connection to match that size, and initialize the terminal with that size.
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
THROW_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
// Fist set up the dx engine with the window size in pixels.
// Then, using the font, get the number of characters that can fit.
// Resize our terminal connection to match that size, and initialize the terminal with that size.
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
THROW_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
// Update DxEngine's SelectionBackground
dxEngine->SetSelectionBackground(_settings.SelectionBackground());
// Update DxEngine's SelectionBackground
dxEngine->SetSelectionBackground(_settings.SelectionBackground());
const auto vp = dxEngine->GetViewportInCharacters(viewInPixels);
const auto width = vp.Width();
const auto height = vp.Height();
_connection.Resize(height, width);
const auto vp = dxEngine->GetViewportInCharacters(viewInPixels);
const auto width = vp.Width();
const auto height = vp.Height();
_connection.Resize(height, width);
// Override the default width and height to match the size of the swapChainPanel
_settings.InitialCols(width);
_settings.InitialRows(height);
// Override the default width and height to match the size of the swapChainPanel
_settings.InitialCols(width);
_settings.InitialRows(height);
_terminal->CreateFromSettings(_settings, renderTarget);
_terminal->CreateFromSettings(_settings, renderTarget);
// TODO:GH#3927 - Make it possible to hot-reload this setting. Right
// here, the setting will only be used when the Terminal is initialized.
dxEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect());
// Tell the DX Engine to notify us when the swap chain changes.
dxEngine->SetCallback(std::bind(&TermControl::SwapChainChanged, this));
// TODO:GH#3927 - hot-reload this one too
// Update DxEngine's AntialiasingMode
switch (_settings.AntialiasingMode())
{
case TextAntialiasingMode::Cleartype:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
break;
case TextAntialiasingMode::Aliased:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
break;
case TextAntialiasingMode::Grayscale:
default:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
break;
}
// TODO:GH#3927 - Make it possible to hot-reload this setting. Right
// here, the setting will only be used when the Terminal is initialized.
dxEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect());
THROW_IF_FAILED(dxEngine->Enable());
_renderEngine = std::move(dxEngine);
// TODO:GH#3927 - hot-reload this one too
// Update DxEngine's AntialiasingMode
switch (_settings.AntialiasingMode())
{
case TextAntialiasingMode::Cleartype:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
break;
case TextAntialiasingMode::Aliased:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
break;
case TextAntialiasingMode::Grayscale:
default:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
break;
}
_AttachDxgiSwapChainToXaml(_renderEngine->GetSwapChain().Get());
THROW_IF_FAILED(dxEngine->Enable());
_renderEngine = std::move(dxEngine);
// Tell the DX Engine to notify us when the swap chain changes.
// We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems)
_renderEngine->SetCallback(std::bind(&TermControl::RenderEngineSwapChainChanged, this));
// This event is explicitly revoked in the destructor: does not need weak_ref
auto onReceiveOutputFn = [this](const hstring str) {
_terminal->Write(str);
};
_connectionOutputEventToken = _connection.TerminalOutput(onReceiveOutputFn);
auto bottom = _terminal->GetViewport().BottomExclusive();
auto bufferHeight = bottom;
auto inputFn = std::bind(&TermControl::_SendInputToConnection, this, std::placeholders::_1);
_terminal->SetWriteInputCallback(inputFn);
ScrollBar().Maximum(bufferHeight - bufferHeight);
ScrollBar().Minimum(0);
ScrollBar().Value(0);
ScrollBar().ViewportSize(bufferHeight);
_SwapChainRoutine();
localPointerToThread->EnablePainting();
// Set up the height of the ScrollViewer and the grid we're using to fake our scrolling height
auto bottom = _terminal->GetViewport().BottomExclusive();
auto bufferHeight = bottom;
// Set up blinking cursor
int blinkTime = GetCaretBlinkTime();
if (blinkTime != INFINITE)
{
// Create a timer
DispatcherTimer cursorTimer;
cursorTimer.Interval(std::chrono::milliseconds(blinkTime));
cursorTimer.Tick({ get_weak(), &TermControl::_CursorTimerTick });
cursorTimer.Start();
_cursorTimer.emplace(std::move(cursorTimer));
}
else
{
// The user has disabled cursor blinking
_cursorTimer = std::nullopt;
}
ScrollBar().Maximum(bufferHeight - bufferHeight);
ScrollBar().Minimum(0);
ScrollBar().Value(0);
ScrollBar().ViewportSize(bufferHeight);
// import value from WinUser (convert from milli-seconds to micro-seconds)
_multiClickTimer = GetDoubleClickTime() * 1000;
localPointerToThread->EnablePainting();
// 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);
auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1);
_terminal->SetTitleChangedCallback(pfnTitleChanged);
_connection.Start();
_initializedTerminal = true;
} // scope for TerminalLock
// call this event dispatcher outside of lock
auto pfnBackgroundColorChanged = std::bind(&TermControl::_BackgroundColorChanged, this, std::placeholders::_1);
_terminal->SetBackgroundCallback(pfnBackgroundColorChanged);
auto pfnScrollPositionChanged = std::bind(&TermControl::_TerminalScrollPositionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
_terminal->SetScrollPositionChangedCallback(pfnScrollPositionChanged);
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000));
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
_autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll });
// Set up blinking cursor
int blinkTime = GetCaretBlinkTime();
if (blinkTime != INFINITE)
{
// Create a timer
_cursorTimer = std::make_optional(DispatcherTimer());
_cursorTimer.value().Interval(std::chrono::milliseconds(blinkTime));
_cursorTimer.value().Tick({ get_weak(), &TermControl::_BlinkCursor });
_cursorTimer.value().Start();
}
else
{
// The user has disabled cursor blinking
_cursorTimer = std::nullopt;
}
// import value from WinUser (convert from milli-seconds to micro-seconds)
_multiClickTimer = GetDoubleClickTime() * 1000;
// 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);
_connection.Start();
_initializedTerminal = true;
_InitializedHandlers(*this, nullptr);
return true;
}
@@ -877,10 +879,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - point: the PointerPoint object representing a mouse event from our XAML input handler
bool TermControl::_CanSendVTMouseInput()
{
if (!_terminal)
{
return false;
}
// If the user is holding down Shift, suppress mouse events
// TODO GH#4875: disable/customize this functionality
const auto modifiers = _GetPressedModifierKeys();
@@ -899,11 +897,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_PointerPressedHandler(Windows::Foundation::IInspectable const& sender,
Input::PointerRoutedEventArgs const& args)
{
if (_closing)
{
return;
}
_CapturePointer(sender, args);
const auto ptr = args.Pointer();
@@ -918,6 +911,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (!_focused)
{
Focus(FocusState::Pointer);
// Save the click position that brought this control into focus
// in case the user wants to perform a click-drag selection.
_focusRaisedClickPos = point.Position();
}
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
@@ -937,6 +933,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (point.Properties().IsLeftButtonPressed())
{
// A single left click from out of focus should only focus.
if (_focusRaisedClickPos)
{
args.Handled(true);
return;
}
const auto cursorPosition = point.Position();
const auto terminalPosition = _GetTerminalPosition(cursorPosition);
@@ -953,26 +956,20 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (multiClickMapper == 3)
{
_terminal->MultiClickSelection(terminalPosition, ::Terminal::SelectionExpansionMode::Line);
_selectionNeedsToBeCopied = true;
}
else if (multiClickMapper == 2)
{
_terminal->MultiClickSelection(terminalPosition, ::Terminal::SelectionExpansionMode::Word);
_selectionNeedsToBeCopied = true;
}
else
{
if (shiftEnabled && _terminal->IsSelectionActive())
{
_terminal->SetSelectionEnd(terminalPosition, ::Terminal::SelectionExpansionMode::Cell);
_selectionNeedsToBeCopied = true;
}
else
{
// A single click down resets the selection and begins a new one.
_terminal->ClearSelection();
_singleClickTouchdownPos = cursorPosition;
_selectionNeedsToBeCopied = false; // there's no selection, so there's nothing to update
_terminal->SetSelectionAnchor(terminalPosition);
}
_lastMouseClickTimestamp = point.Timestamp();
@@ -983,7 +980,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
else if (point.Properties().IsRightButtonPressed())
{
// CopyOnSelect right click always pastes
if (_settings.CopyOnSelect() || !_terminal->IsSelectionActive())
if (_terminal->IsCopyOnSelectActive() || !_terminal->IsSelectionActive())
{
PasteTextFromClipboard();
}
@@ -1011,11 +1008,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_PointerMovedHandler(Windows::Foundation::IInspectable const& /*sender*/,
Input::PointerRoutedEventArgs const& args)
{
if (_closing)
{
return;
}
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(*this);
@@ -1036,22 +1028,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (point.Properties().IsLeftButtonPressed())
{
const auto cursorPosition = point.Position();
_clickDrag = true;
if (_singleClickTouchdownPos)
// PointerPressedHandler doesn't set the SelectionAnchor when the click was
// from out of focus, so PointerMoved has to set it.
if (_focusRaisedClickPos)
{
// Figure out if the user's moved a quarter of a cell's smaller axis away from the clickdown point
auto& touchdownPoint{ *_singleClickTouchdownPos };
auto distance{ std::sqrtf(std::powf(cursorPosition.X - touchdownPoint.X, 2) + std::powf(cursorPosition.Y - touchdownPoint.Y, 2)) };
const auto fontSize{ _actualFont.GetSize() };
if (distance >= (std::min(fontSize.X, fontSize.Y) / 4.f))
{
_terminal->SetSelectionAnchor(_GetTerminalPosition(touchdownPoint));
// stop tracking the touchdown point
_singleClickTouchdownPos = std::nullopt;
}
_terminal->SetSelectionAnchor(_GetTerminalPosition(*_focusRaisedClickPos));
}
const auto cursorPosition = point.Position();
_SetEndSelectionPointAtCursor(cursorPosition);
const double cursorBelowBottomDist = cursorPosition.Y - SwapChainPanel().Margin().Top - SwapChainPanel().ActualHeight();
@@ -1119,11 +1105,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_PointerReleasedHandler(Windows::Foundation::IInspectable const& sender,
Input::PointerRoutedEventArgs const& args)
{
if (_closing)
{
return;
}
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(*this);
@@ -1140,14 +1121,17 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Only a left click release when copy on select is active should perform a copy.
// Right clicks and middle clicks should not need to do anything when released.
if (_settings.CopyOnSelect() && point.Properties().PointerUpdateKind() == Windows::UI::Input::PointerUpdateKind::LeftButtonReleased)
if (_terminal->IsCopyOnSelectActive() && point.Properties().PointerUpdateKind() == Windows::UI::Input::PointerUpdateKind::LeftButtonReleased)
{
const auto modifiers = static_cast<uint32_t>(args.KeyModifiers());
// static_cast to a uint32_t because we can't use the WI_IsFlagSet
// macro directly with a VirtualKeyModifiers
const auto shiftEnabled = WI_IsFlagSet(modifiers, static_cast<uint32_t>(VirtualKeyModifiers::Shift));
if (_selectionNeedsToBeCopied)
// In a Copy on Select scenario,
// All left click drags should copy,
// All left clicks on a focused control should copy if a selection is active.
if (_clickDrag || !_focusRaisedClickPos)
{
CopySelectionToClipboard(shiftEnabled);
}
@@ -1158,7 +1142,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_touchAnchor = std::nullopt;
}
_singleClickTouchdownPos = std::nullopt;
_focusRaisedClickPos = std::nullopt;
_clickDrag = false;
_TryStopAutoScroll(ptr.PointerId());
@@ -1174,11 +1159,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_MouseWheelHandler(Windows::Foundation::IInspectable const& /*sender*/,
Input::PointerRoutedEventArgs const& args)
{
if (_closing)
{
return;
}
const auto point = args.GetCurrentPoint(*this);
if (_CanSendVTMouseInput())
@@ -1309,7 +1289,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& /*sender*/,
Controls::Primitives::RangeBaseValueChangedEventArgs const& args)
{
if (_isTerminalInitiatedScroll || _closing)
if (_isTerminalInitiatedScroll)
{
return;
}
@@ -1494,7 +1474,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
return;
}
_focused = false;
if (_uiaEngine.get())
@@ -1566,6 +1545,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// concerned with initialization process. Value forwarded to event handler.
void TermControl::_UpdateFont(const bool initialUpdate)
{
auto lock = _terminal->LockForWriting();
const int newDpi = static_cast<int>(static_cast<double>(USER_DEFAULT_SCREEN_DPI) * SwapChainPanel().CompositionScaleX());
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
@@ -1592,12 +1573,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Refresh our font with the renderer
_UpdateFont();
auto lock = _terminal->LockForWriting();
// Resize the terminal's BUFFER to match the new font size. This does
// NOT change the size of the window, because that can lead to more
// problems (like what happens when you change the font size while the
// window is maximized?)
auto lock = _terminal->LockForWriting();
_DoResize(SwapChainPanel().ActualWidth(), SwapChainPanel().ActualHeight());
}
CATCH_LOG();
@@ -1611,7 +1591,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_SwapChainSizeChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/,
SizeChangedEventArgs const& e)
{
if (!_initializedTerminal || _closing)
if (!_initializedTerminal)
{
return;
}
@@ -1642,8 +1622,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Arguments:
// - sender: not used
// - e: not used
void TermControl::_CursorTimerTick(Windows::Foundation::IInspectable const& /* sender */,
Windows::Foundation::IInspectable const& /* e */)
void TermControl::_BlinkCursor(Windows::Foundation::IInspectable const& /* sender */,
Windows::Foundation::IInspectable const& /* e */)
{
if ((_closing) || (!_terminal->IsCursorBlinkingAllowed() && _terminal->IsCursorVisible()))
{
@@ -1669,7 +1649,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// save location (for rendering) + render
_terminal->SetSelectionEnd(terminalPosition);
_renderer->TriggerSelection();
_selectionNeedsToBeCopied = true;
}
// Method Description:
@@ -1795,7 +1774,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
hstring TermControl::Title()
{
hstring hstr{ _terminal->GetConsoleTitle() };
if (!_initializedTerminal)
return L"";
hstring hstr(_terminal->GetConsoleTitle());
return hstr;
}
@@ -1812,20 +1794,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - collapseText: collapse all of the text to one line
bool TermControl::CopySelectionToClipboard(bool collapseText)
{
if (_closing)
{
return false;
}
// no selection --> nothing to copy
if (!_terminal->IsSelectionActive())
if (_terminal == nullptr || !_terminal->IsSelectionActive())
{
return false;
}
// Mark the current selection as copied
_selectionNeedsToBeCopied = false;
// extract text from buffer
const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(collapseText);
@@ -1849,7 +1822,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_actualFont.GetFaceName(),
_settings.DefaultBackground());
if (!_settings.CopyOnSelect())
if (!_terminal->IsCopyOnSelectActive())
{
_terminal->ClearSelection();
_renderer->TriggerSelection();
@@ -1885,7 +1858,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_connectionStateChangedRevoker.revoke();
TSFInputControl().Close(); // Disconnect the TSF input control so it doesn't receive EditContext events.
_autoScrollTimer.Stop();
if (auto localConnection{ std::exchange(_connection, nullptr) })
{
@@ -1903,8 +1875,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// renderEngine is destroyed
}
// we don't destroy _terminal here; it now has the same lifetime as the
// control.
if (auto localTerminal{ std::exchange(_terminal, nullptr) })
{
_initializedTerminal = false;
// terminal is destroyed.
}
}
}
@@ -2218,11 +2193,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControl::_CompositionCompleted(winrt::hstring text)
{
if (_closing)
{
return;
}
_connection.WriteInput(text);
}
@@ -2235,14 +2205,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControl::_CurrentCursorPositionHandler(const IInspectable& /*sender*/, const CursorPositionEventArgs& eventArgs)
{
auto lock = _terminal->LockForReading();
if (!_initializedTerminal)
// If we haven't initialized yet, just quick return.
if (!_terminal)
{
// fake it
eventArgs.CurrentPosition({ 0, 0 });
return;
}
const COORD cursorPos = _terminal->GetCursorPosition();
Windows::Foundation::Point p = { gsl::narrow_cast<float>(cursorPos.X), gsl::narrow_cast<float>(cursorPos.Y) };
eventArgs.CurrentPosition(p);
@@ -2312,14 +2279,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - e: The DragEventArgs from the Drop event
// Return Value:
// - <none>
winrt::fire_and_forget TermControl::_DragDropHandler(Windows::Foundation::IInspectable const& /*sender*/,
DragEventArgs const e)
winrt::fire_and_forget TermControl::_DoDragDrop(DragEventArgs const e)
{
if (_closing)
{
return;
}
if (e.DataView().Contains(StandardDataFormats::StorageItems()))
{
auto items = co_await e.DataView().GetStorageItemsAsync();
@@ -2354,6 +2315,22 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
// Method Description:
// - Synchronous handler for the "Drop" event. We'll dispatch the async
// _DoDragDrop method to handle this, because getting information about
// the file that was potentially dropped onto us must be done off the UI
// thread.
// Arguments:
// - e: The DragEventArgs from the Drop event
// Return Value:
// - <none>
void TermControl::_DragDropHandler(Windows::Foundation::IInspectable const& /*sender*/,
DragEventArgs const& e)
{
// Dispatch an async method to handle the drop event.
_DoDragDrop(e);
}
// Method Description:
// - Handle the DragOver event. We'll signal that the drag operation we
// support is the "copy" operation, and we'll also customize the
@@ -2367,11 +2344,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_DragOverHandler(Windows::Foundation::IInspectable const& /*sender*/,
DragEventArgs const& e)
{
if (_closing)
{
return;
}
if (!e.DataView().Contains(StandardDataFormats::StorageItems()))
{
// We can't do anything for non-storageitems right now.
@@ -2387,7 +2359,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
e.DragUIOverride().IsCaptionVisible(true);
// Sets if the dragged content is visible
e.DragUIOverride().IsContentVisible(false);
// Sets if the glyph is visible
// Sets if the glyph is visibile
e.DragUIOverride().IsGlyphVisible(false);
}

View File

@@ -77,8 +77,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void AdjustFontSize(int fontSizeDelta);
void ResetFontSize();
winrt::fire_and_forget RenderEngineSwapChainChanged();
void _AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain);
winrt::fire_and_forget SwapChainChanged();
void CreateSearchBoxControl();
@@ -156,15 +155,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// imported from WinUser
// Used for PointerPoint.Timestamp Property (https://docs.microsoft.com/en-us/uwp/api/windows.ui.input.pointerpoint.timestamp#Windows_UI_Input_PointerPoint_Timestamp)
Timestamp _multiClickTimer;
unsigned int _multiClickCounter;
Timestamp _lastMouseClickTimestamp;
unsigned int _multiClickCounter;
std::optional<winrt::Windows::Foundation::Point> _lastMouseClickPos;
std::optional<winrt::Windows::Foundation::Point> _singleClickTouchdownPos;
// This field tracks whether the selection has changed meaningfully
// since it was last copied. It's generally used to prevent copyOnSelect
// from firing when the pointer _just happens_ to be released over the
// terminal.
bool _selectionNeedsToBeCopied;
std::optional<winrt::Windows::Foundation::Point> _focusRaisedClickPos;
bool _clickDrag;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
@@ -184,13 +180,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void _ScrollbarChangeHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e);
void _GotFocusHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
void _LostFocusHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
winrt::fire_and_forget _DragDropHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::DragEventArgs const e);
void _DragDropHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::DragEventArgs const& e);
void _DragOverHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::DragEventArgs const& e);
winrt::fire_and_forget _DoDragDrop(Windows::UI::Xaml::DragEventArgs const e);
void _CursorTimerTick(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
void _BlinkCursor(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
void _SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition);
void _SendInputToConnection(const std::wstring& wstr);
void _SendPastedTextToConnection(const std::wstring& wstr);
winrt::fire_and_forget _SwapChainRoutine();
void _SwapChainSizeChanged(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::SizeChangedEventArgs const& e);
void _SwapChainScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender, Windows::Foundation::IInspectable const& args);
void _DoResize(const double newWidth, const double newHeight);

View File

@@ -85,7 +85,7 @@
<None Include="TerminalControl.def" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources\*\Resources.resw" />
<PRIResource Include="Resources/Resources.language-en.resw" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>

View File

@@ -45,7 +45,7 @@
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources\*\Resources.resw">
<PRIResource Include="Resources/Resources.language-en.resw">
<Filter>Resources</Filter>
</PRIResource>
</ItemGroup>

View File

@@ -45,7 +45,9 @@ Terminal::Terminal() :
_scrollOffset{ 0 },
_snapOnInput{ true },
_blockSelection{ false },
_selection{ std::nullopt }
_selection{ std::nullopt },
_allowSingleCharSelection{ true },
_copyOnSelect{ false }
{
auto dispatch = std::make_unique<TerminalDispatch>(*this);
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
@@ -143,6 +145,8 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
_wordDelimiters = settings.WordDelimiters();
_copyOnSelect = settings.CopyOnSelect();
_suppressApplicationTitle = settings.SuppressApplicationTitle();
_startingTitle = settings.StartingTitle();
@@ -207,7 +211,7 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
// requires a bit of trickiness to remain consistent with conpty's
// buffer (as seen below).
// * the new value of visibleViewportTop will be used to calculate the
// new scrollOffset in the new buffer, so that the visible lines on
// new scrollOffsett in the new buffer, so that the visible lines on
// the screen remain roughly the same.
TextBuffer::PositionInformation oldRows{ 0 };
oldRows.mutableViewportTop = oldViewportTop;
@@ -701,7 +705,6 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
if (notifyScroll)
{
// TODO: don't do this, thanks migrie
_buffer->GetRenderTarget().TriggerRedrawAll();
_NotifyScrollEvent();
}

View File

@@ -181,6 +181,7 @@ public:
Word,
Line
};
const bool IsCopyOnSelectActive() const noexcept;
void MultiClickSelection(const COORD viewportPos, SelectionExpansionMode expansionMode);
void SetSelectionAnchor(const COORD position);
void SetSelectionEnd(const COORD position, std::optional<SelectionExpansionMode> newExpansionMode = std::nullopt);
@@ -221,6 +222,8 @@ private:
};
std::optional<SelectionAnchors> _selection;
bool _blockSelection;
bool _allowSingleCharSelection;
bool _copyOnSelect;
std::wstring _wordDelimiters;
SelectionExpansionMode _multiClickSelectionMode;
#pragma endregion
@@ -237,7 +240,7 @@ private:
// If _scrollOffset is 0, then the visible region of the buffer is the viewport.
int _scrollOffset;
// TODO this might not be the value we want to store.
// We might want to store the height in the scrollback that's currently visible.
// We might want to store the height in the scrollback that's currenty visible.
// Think on this some more.
// For example: While looking at the scrollback, we probably want the visible region to "stick"
// to the region they scrolled to. If that were the case, then every time we move _mutableViewport,
@@ -272,6 +275,7 @@ private:
std::pair<COORD, COORD> _PivotSelection(const COORD targetPos) const;
std::pair<COORD, COORD> _ExpandSelectionAnchors(std::pair<COORD, COORD> anchors) const;
COORD _ConvertToBufferCell(const COORD viewportPos) const;
const bool _IsSingleCellSelection() const noexcept;
#pragma endregion
#ifdef UNIT_TESTING

View File

@@ -396,7 +396,7 @@ try
return false;
}
// Move the viewport, adjust the scroll bar if needed, and restore the old cursor position
// Move the viewport, adjust the scoll bar if needed, and restore the old cursor position
_mutableViewport = Viewport::FromExclusive(newWin);
Terminal::_NotifyScrollEvent();
SetCursorPosition(relativeCursor.X, relativeCursor.Y);

View File

@@ -82,12 +82,27 @@ const COORD Terminal::GetSelectionEnd() const noexcept
return _selection->end;
}
// Method Description:
// - Checks if selection is on a single cell
// Return Value:
// - bool representing if selection is only a single cell. Used for copyOnSelect
const bool Terminal::_IsSingleCellSelection() const noexcept
{
return (_selection->start == _selection->end);
}
// Method Description:
// - Checks if selection is active
// Return Value:
// - bool representing if selection is active. Used to decide copy/paste on right click
const bool Terminal::IsSelectionActive() const noexcept
{
// A single cell selection is not considered an active selection,
// if it's not allowed
if (!_allowSingleCharSelection && _IsSingleCellSelection())
{
return false;
}
return _selection.has_value();
}
@@ -96,6 +111,15 @@ const bool Terminal::IsBlockSelection() const noexcept
return _blockSelection;
}
// Method Description:
// - Checks if the CopyOnSelect setting is active
// Return Value:
// - true if feature is active, false otherwise.
const bool Terminal::IsCopyOnSelectActive() const noexcept
{
return _copyOnSelect;
}
// Method Description:
// - Perform a multi-click selection at viewportPos expanding according to the expansionMode
// Arguments:
@@ -124,6 +148,8 @@ void Terminal::SetSelectionAnchor(const COORD viewportPos)
_selection = SelectionAnchors{};
_selection->pivot = _ConvertToBufferCell(viewportPos);
_allowSingleCharSelection = (_copyOnSelect) ? false : true;
_multiClickSelectionMode = SelectionExpansionMode::Cell;
SetSelectionEnd(viewportPos);
@@ -146,6 +172,13 @@ void Terminal::SetSelectionEnd(const COORD viewportPos, std::optional<SelectionE
const auto anchors = _PivotSelection(textBufferPos);
std::tie(_selection->start, _selection->end) = _ExpandSelectionAnchors(anchors);
// moving the endpoint of what used to be a single cell selection
// allows the user to drag back and select just one cell
if (_copyOnSelect && !_IsSingleCellSelection())
{
_allowSingleCharSelection = true;
}
}
// Method Description:
@@ -215,6 +248,7 @@ void Terminal::SetBlockSelection(const bool isEnabled) noexcept
#pragma warning(disable : 26440) // changing this to noexcept would require a change to ConHost's selection model
void Terminal::ClearSelection()
{
_allowSingleCharSelection = false;
_selection = std::nullopt;
}

View File

@@ -38,8 +38,6 @@ namespace Microsoft.Terminal.Settings
IKeyBindings KeyBindings;
Boolean CopyOnSelect;
String Commandline;
String StartingDirectory;
String EnvironmentVariables;

View File

@@ -32,6 +32,7 @@ namespace Microsoft.Terminal.Settings
String StartingTitle;
Boolean SuppressApplicationTitle;
String WordDelimiters;
Boolean CopyOnSelect;
};
}

View File

@@ -53,9 +53,6 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
static const SHORT TerminalViewWidth = 80;
static const SHORT TerminalViewHeight = 32;
// This test class is for tests that are supposed to emit something in the PTY layer
// and then check that they've been staged for presentation correctly inside
// the Terminal application. Which sequences were used to get here don't matter.
TEST_CLASS(ConptyRoundtripTests);
TEST_CLASS_SETUP(ClassSetup)
@@ -174,8 +171,6 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
TEST_METHOD(TestResizeHeight);
TEST_METHOD(ScrollWithMargins);
private:
bool _writeCallback(const char* const pch, size_t const cch);
void _flushFirstFrame();
@@ -476,7 +471,7 @@ void ConptyRoundtripTests::TestExactWrappingWithoutSpaces()
//
// If a line _exactly_ wraps to the next line, we can't tell if the line
// should really wrap, or manually break. The client app is writing a line
// that's exactly the width of the buffer that manually breaks at the
// that's exactly the width of the buffer that manually linebreaked at the
// end of the line, followed by another line.
//
// With the old PaintBufferLine interface, there's no way to know if this
@@ -633,7 +628,7 @@ void ConptyRoundtripTests::MoveCursorAtEOL()
// might change, but the buffer contents shouldn't.
// If they do change and these tests break, that's to be expected.
expectedOutput.push_back(std::string(TerminalViewWidth, 'A'));
expectedOutput.push_back("\x1b[1;80H");
// expectedOutput.push_back("\x1b[1;80H");
VERIFY_SUCCEEDED(renderer.PaintFrame());
@@ -659,8 +654,11 @@ void ConptyRoundtripTests::MoveCursorAtEOL()
verifyData1(hostTb);
// expectedOutput.push_back(" ");
// expectedOutput.push_back("\x1b[1;80H");
expectedOutput.push_back("\x08");
expectedOutput.push_back(" ");
expectedOutput.push_back("\x1b[1;80H");
expectedOutput.push_back("\x08");
VERIFY_SUCCEEDED(renderer.PaintFrame());
verifyData1(termTb);
@@ -950,7 +948,7 @@ void ConptyRoundtripTests::PassthroughClearScrollback()
else
{
// After we hit the bottom of the viewport, the newlines come in
// separated for whatever reason.
// seperated for whatever reason.
expectedOutput.push_back("\r");
expectedOutput.push_back("\n");
expectedOutput.push_back("");
@@ -1060,245 +1058,3 @@ void ConptyRoundtripTests::PassthroughHardReset()
TestUtils::VerifyExpectedString(termTb, std::wstring(TerminalViewWidth, L' '), { 0, y });
}
}
void ConptyRoundtripTests::ScrollWithMargins()
{
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
const auto initialTermView = term->GetViewport();
Log::Comment(L"Flush first frame.");
_flushFirstFrame();
// Fill up the buffer with some text.
// We're going to write something like this:
// AAAA
// BBBB
// CCCC
// ........
// QQQQ
// ****************
// The letters represent the data in the TMUX pane.
// The final *** line represents the mode line which we will
// attempt to hold in place and not scroll.
Log::Comment(L"Fill host with text pattern by feeding it into VT parser.");
const auto rowsToWrite = initialTermView.Height() - 1;
// For all lines but the last one, write out a few of a letter.
for (auto i = 0; i < rowsToWrite; ++i)
{
const wchar_t wch = static_cast<wchar_t>(L'A' + i);
hostSm.ProcessCharacter(wch);
hostSm.ProcessCharacter(wch);
hostSm.ProcessCharacter(wch);
hostSm.ProcessCharacter(wch);
hostSm.ProcessCharacter('\n');
}
// For the last one, write out the asterisks for the mode line.
for (auto i = 0; i < initialTermView.Width(); ++i)
{
hostSm.ProcessCharacter('*');
}
// no newline in the bottom right corner or it will move unexpectedly.
// Now set up the verification that the buffers are full of the pattern we expect.
// This function will verify the text backing buffers.
auto verifyBuffer = [&](const TextBuffer& tb) {
auto& cursor = tb.GetCursor();
// Verify the cursor is waiting in the bottom right corner
VERIFY_ARE_EQUAL(initialTermView.Height() - 1, cursor.GetPosition().Y);
VERIFY_ARE_EQUAL(initialTermView.Width() - 1, cursor.GetPosition().X);
// For all rows except the last one, verify that we have a run of four letters.
for (auto i = 0; i < rowsToWrite; ++i)
{
const std::wstring expectedString(4, static_cast<wchar_t>(L'A' + i));
const COORD expectedPos{ 0, gsl::narrow<SHORT>(i) };
TestUtils::VerifyExpectedString(tb, expectedString, expectedPos);
}
// For the last row, verify we have an entire row of asterisks for the mode line.
const std::wstring expectedModeLine(initialTermView.Width(), L'*');
const COORD expectedPos{ 0, gsl::narrow<SHORT>(rowsToWrite) };
TestUtils::VerifyExpectedString(tb, expectedModeLine, expectedPos);
};
// This will verify the text emitted from the PTY.
for (auto i = 0; i < rowsToWrite; ++i)
{
const std::string expectedString(4, static_cast<char>('A' + i));
expectedOutput.push_back(expectedString);
expectedOutput.push_back("\r\n");
}
{
const std::string expectedString(initialTermView.Width(), '*');
expectedOutput.push_back(expectedString);
// Cursor gets reset into bottom right corner as we're writing all the way into that corner.
std::stringstream ss;
ss << "\x1b[" << initialTermView.Height() << ";" << initialTermView.Width() << "H";
expectedOutput.push_back(ss.str());
}
Log::Comment(L"Verify host buffer contains pattern.");
// Verify the host side.
verifyBuffer(hostTb);
Log::Comment(L"Emit PTY frame and validate it transmits the right data.");
// Paint the frame
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"Verify terminal buffer contains pattern.");
// Verify the terminal side.
verifyBuffer(termTb);
Log::Comment(L"!!! OK. Set up the scroll region and let's get scrolling!");
// This is a simulation of what TMUX does to scroll everything except the mode line.
// First build up our VT strings...
std::wstring reducedScrollRegion;
{
std::wstringstream wss;
// For 20 tall buffer...
// ESC[1;19r
// Set scroll region to lines 1-19.
wss << L"\x1b[1;" << initialTermView.Height() - 1 << L"r";
reducedScrollRegion = wss.str();
}
std::wstring completeScrollRegion;
{
std::wstringstream wss;
// For 20 tall buffer...
// ESC[1;20r
// Set scroll region to lines 1-20. (or the whole buffer)
wss << L"\x1b[1;" << initialTermView.Height() << L"r";
completeScrollRegion = wss.str();
}
std::wstring reducedCursorBottomRight;
{
std::wstringstream wss;
// For 20 tall and 100 wide buffer
// ESC[19;100H
// Put cursor on line 19 (1 before last) and the right most column 100.
// (Remember that in VT, we start counting from 1 not 0.)
wss << L"\x1b[" << initialTermView.Height() - 1 << L";" << initialTermView.Width() << "H";
reducedCursorBottomRight = wss.str();
}
std::wstring completeCursorAtPromptLine;
{
std::wstringstream wss;
// For 20 tall and 100 wide buffer
// ESC[19;1H
// Put cursor on line 19 (1 before last) and the left most column 1.
// (Remember that in VT, we start counting from 1 not 0.)
wss << L"\x1b[" << initialTermView.Height() - 1 << L";1H";
completeCursorAtPromptLine = wss.str();
}
Log::Comment(L"Perform all the operations on the buffer.");
// OK this is what TMUX does.
// 1. Mark off the scroll area as everything but the mode line.
hostSm.ProcessString(reducedScrollRegion);
// 2. Put the cursor in the bottom-right corner of the scroll region.
hostSm.ProcessString(reducedCursorBottomRight);
// 3. Send a single newline which should do the heavy lifting
// of pushing everything in the scroll region up by 1 line and
// leave everything outside the region alone.
// This entire block is subject to change in the future with optimizations.
{
// Cursor gets redrawn in the bottom right of the scroll region with the repaint that is forced
// early while the screen is rotated.
std::stringstream ss;
ss << "\x1b[" << initialTermView.Height() - 1 << ";" << initialTermView.Width() << "H";
expectedOutput.push_back(ss.str());
expectedOutput.push_back("\x1b[?25h"); // turn the cursor back on too.
}
hostSm.ProcessString(L"\n");
// 4. Remove the scroll area by setting it to the entire size of the viewport.
hostSm.ProcessString(completeScrollRegion);
// 5. Put the cursor back at the beginning of the new line that was just revealed.
hostSm.ProcessString(completeCursorAtPromptLine);
// Set up the verifications like above.
auto verifyBufferAfter = [&](const TextBuffer& tb) {
auto& cursor = tb.GetCursor();
// Verify the cursor is waiting on the freshly revealed line (1 above mode line)
// and in the left most column.
VERIFY_ARE_EQUAL(initialTermView.Height() - 2, cursor.GetPosition().Y);
VERIFY_ARE_EQUAL(0, cursor.GetPosition().X);
// For all rows except the last two, verify that we have a run of four letters.
for (auto i = 0; i < rowsToWrite - 1; ++i)
{
// Start with B this time because the A line got scrolled off the top.
const std::wstring expectedString(4, static_cast<wchar_t>(L'B' + i));
const COORD expectedPos{ 0, gsl::narrow<SHORT>(i) };
TestUtils::VerifyExpectedString(tb, expectedString, expectedPos);
}
// For the second to last row, verify that it is blank.
{
const std::wstring expectedBlankLine(initialTermView.Width(), L' ');
const COORD blankLinePos{ 0, gsl::narrow<SHORT>(rowsToWrite - 1) };
TestUtils::VerifyExpectedString(tb, expectedBlankLine, blankLinePos);
}
// For the last row, verify we have an entire row of asterisks for the mode line.
{
const std::wstring expectedModeLine(initialTermView.Width(), L'*');
const COORD modeLinePos{ 0, gsl::narrow<SHORT>(rowsToWrite) };
TestUtils::VerifyExpectedString(tb, expectedModeLine, modeLinePos);
}
};
// This will verify the text emitted from the PTY.
expectedOutput.push_back("\x1b[H"); // cursor returns to top left corner.
for (auto i = 0; i < rowsToWrite - 1; ++i)
{
const std::string expectedString(4, static_cast<char>('B' + i));
expectedOutput.push_back(expectedString);
expectedOutput.push_back("\x1b[K"); // erase the rest of the line.
expectedOutput.push_back("\r\n");
}
{
expectedOutput.push_back(""); // nothing for the empty line
expectedOutput.push_back("\x1b[K"); // erase the rest of the line.
expectedOutput.push_back("\r\n");
}
{
const std::string expectedString(initialTermView.Width(), '*');
expectedOutput.push_back(expectedString);
}
{
// Cursor gets reset into second line from bottom, left most column
std::stringstream ss;
ss << "\x1b[" << initialTermView.Height() - 1 << ";1H";
expectedOutput.push_back(ss.str());
}
expectedOutput.push_back("\x1b[?25h"); // turn the cursor back on too.
Log::Comment(L"Verify host buffer contains pattern moved up one and mode line still in place.");
// Verify the host side.
verifyBufferAfter(hostTb);
Log::Comment(L"Emit PTY frame and validate it transmits the right data.");
// Paint the frame
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"Verify terminal buffer contains pattern moved up one and mode line still in place.");
// Verify the terminal side.
verifyBufferAfter(termTb);
}

View File

@@ -672,5 +672,49 @@ namespace TerminalCoreUnitTests
selection = term.GetViewport().ConvertToOrigin(selectionRects.at(1)).ToInclusive();
VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 0, 11, 99, 11 }));
}
TEST_METHOD(CopyOnSelect)
{
Terminal term;
DummyRenderTarget emptyRT;
term.Create({ 100, 100 }, 0, emptyRT);
// set copyOnSelect for terminal
auto settings = winrt::make<MockTermSettings>(0, 100, 100);
settings.CopyOnSelect(true);
term.UpdateSettings(settings);
// Simulate click at (x,y) = (5,10)
term.SetSelectionAnchor({ 5, 10 });
// Simulate move to (x,y) = (5,10)
// (So, no movement)
term.SetSelectionEnd({ 5, 10 });
// Case 1: single cell selection not allowed
{
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
// Validate selection area
VERIFY_ARE_EQUAL(selectionRects.size(), static_cast<size_t>(0));
// single cell selection should not be allowed
// thus, selection is NOT active
VERIFY_IS_FALSE(term.IsSelectionActive());
}
// Case 2: move off of single cell
term.SetSelectionEnd({ 6, 10 });
ValidateSingleRowSelection(term, { 5, 10, 6, 10 });
VERIFY_IS_TRUE(term.IsSelectionActive());
// Case 3: move back onto single cell (now allowed)
term.SetSelectionEnd({ 5, 10 });
ValidateSingleRowSelection(term, { 5, 10, 5, 10 });
// single cell selection should now be allowed
VERIFY_IS_TRUE(term.IsSelectionActive());
}
};
}

View File

@@ -403,7 +403,7 @@ int NonClientIslandWindow::_GetResizeHandleHeight() const noexcept
// However, testing a bunch of other apps with fullscreen modes
// and an auto-hiding taskbar has shown that _none_ of them
// reveal the taskbar from fullscreen mode. This includes Edge,
// Firefox, Chrome, Sublime Text, PowerPoint - none seemed to
// Firefox, Chrome, Sublime Text, Powerpoint - none seemed to
// support this.
//
// This does however work fine for maximized.

View File

@@ -123,7 +123,7 @@
<!-- Resources -->
<!-- This resw only defines things that are used in this package's AppxManifest,
so it's not in the common resource items. -->
<PRIResource Include="Resources\*\Resources.resw" />
<PRIResource Include="Resources\Resources.language-en.resw" />
<PRIResource Include="Resources\Resources.resw" />
<PRIResource Include="Resources\Resources.devicefamily-core.resw" />
</ItemGroup>

View File

@@ -18,12 +18,15 @@
<Filter Include="Resources">
<UniqueIdentifier>{4718a03b-f768-4d96-9106-096573a315a3}</UniqueIdentifier>
</Filter>
<Filter Include="Resources\en-US">
<UniqueIdentifier>{943541d5-c1da-435d-9f61-85d1b118f041}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources.devicefamily-core.resw">
<Filter>Resources</Filter>
</PRIResource>
<PRIResource Include="Resources\*\Resources.resw">
<PRIResource Include="Resources.language-en.resw">
<Filter>Resources</Filter>
</PRIResource>
<PRIResource Include="Resources.resw">

View File

@@ -38,7 +38,7 @@ namespace Microsoft.Terminal.Wpf
WM_MOUSEACTIVATE = 0x0021,
WM_GETOBJECT = 0x003D,
/// <summary>
/// The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place in the Z order has changed as a result of a call to the SetWindowPos function or another window-management function.
/// </summary>

View File

@@ -26,7 +26,7 @@ namespace Microsoft.Terminal.Wpf
private DispatcherTimer blinkTimer;
private NativeMethods.ScrollCallback scrollCallback;
private NativeMethods.WriteCallback writeCallback;
/// <summary>
/// Initializes a new instance of the <see cref="TerminalContainer"/> class.
/// </summary>
@@ -35,7 +35,7 @@ namespace Microsoft.Terminal.Wpf
this.MessageHook += this.TerminalContainer_MessageHook;
this.GotFocus += this.TerminalContainer_GotFocus;
this.Focusable = true;
var blinkTime = NativeMethods.GetCaretBlinkTime();
if (blinkTime != uint.MaxValue)

View File

@@ -9,7 +9,7 @@ namespace Microsoft.Terminal.Wpf
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
/// <summary>
/// A basic terminal control. This control can receive and render standard VT100 sequences.
/// </summary>

View File

@@ -109,7 +109,7 @@ private: \
// Use this macro to quickly implement both the getter and setter for an observable property.
// This is similar to the GETSET_PROPERTY macro above, except this will also raise a
// PropertyChanged event with the name of the property that has changed inside of the setter.
// PropertyChanged event with the name of the property that has changed inside of the settter.
#define OBSERVABLE_GETSET_PROPERTY(type, name, event) \
public: \
type name() { return _##name; }; \

View File

@@ -9,7 +9,7 @@
1. All of our winrt types are included in this manifest, including types from
MUX.dll. Should this list of types ever change, we'll need to manually
update this file. The easiest way of doing that is deploying the app from
VS, then copying the Extensions from the appxmanifest.xml that's generated
VS, then copying the Extensions from the Appxmandifest.xml that's generated
under `src/cascadia/CascadiaPackage/bin/$(platform)/$(configuration)/appx`.
2. We also _NEED_ the two vclibs listed under the `PackageDependency` block.

View File

@@ -171,33 +171,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// displays the correct text.
if (newViewOrigin == viewport.Origin())
{
// Inside this block, we're shifting down at the bottom.
// This means that we had something like this:
// AAAA
// BBBB
// CCCC
// DDDD
// EEEE
//
// Our margins were set for lines A-D, but not on line E.
// So we circled the whole buffer up by one:
// BBBB
// CCCC
// DDDD
// EEEE
// <blank, was AAAA>
//
// Then we scrolled the contents of everything OUTSIDE the margin frame down.
// BBBB
// CCCC
// DDDD
// <blank, filled during scroll down of EEEE>
// EEEE
//
// And now we need to report that only the bottom line didn't "move" as we put the EEEE
// back where it started, but everything else moved.
// In this case, delta was 1. So the amount that moved is the entire viewport height minus the delta.
Viewport invalid = Viewport::FromDimensions(viewport.Origin(), { viewport.Width(), viewport.Height() - delta });
Viewport invalid = Viewport::FromDimensions(viewport.Origin(), { viewport.Width(), delta });
screenInfo.GetRenderTarget().TriggerRedraw(invalid);
}

View File

@@ -481,7 +481,7 @@ namespace Conhost.UIA.Tests
TextPatternRange testRange = visibleRanges.First().Clone();
// assumes that range is a line range at the top of the screen buffer
Action<TextPatternRange> testTopBoundary = delegate (TextPatternRange range)
Action<TextPatternRange> testTopBoundary = delegate(TextPatternRange range)
{
// the first visible range is at the top of the screen
// buffer, we shouldn't be able to move the starting endpoint up

View File

@@ -31,7 +31,7 @@ namespace Conhost.UIA.Tests.Common
~ShortcutHelper()
{
this.Dispose(false);
this.Dispose(false);
}
public void Dispose()

View File

@@ -52,7 +52,7 @@ namespace Conhost.UIA.Tests.Elements
tab.Click();
Globals.WaitForTimeout();
this.PopulateItemsOnNavigate(this.propDialog.PropWindow);
}

View File

@@ -33,7 +33,7 @@ namespace Conhost.UIA.Tests.Elements
this.tabs.Add(new FontTab(this.propDialog));
this.tabs.Add(new LayoutTab(this.propDialog));
this.tabs.Add(new ColorsTab(this.propDialog));
}
private Tabs()

View File

@@ -105,7 +105,7 @@ namespace Conhost.UIA.Tests.Elements
app.UIRoot.SendKeys(Keys.Escape);
this.state = ViewportStates.Normal;
}
public void EnterMode(ViewportStates state)
{
if (state == ViewportStates.Normal)

View File

@@ -111,7 +111,7 @@ namespace Conhost.UIA.Tests
default:
throw new NotSupportedException();
}
afterScroll = app.GetScreenBufferInfo();
switch (dir)

View File

@@ -57,7 +57,7 @@ namespace Conhost.UIA.Tests
reg.BackupRegistry(); // we're going to modify the virtual terminal state for this, so back it up first.
VersionSelector.SetConsoleVersion(reg, ConsoleVersion.V2);
reg.SetDefaultValue(VIRTUAL_TERMINAL_KEY_NAME, VIRTUAL_TERMINAL_ON_VALUE);
bool haveVtAppPath = !string.IsNullOrEmpty(vtAppLocation);
Verify.IsTrue(haveVtAppPath, "Ensure that we passed in the location to VtApp.exe");
@@ -114,12 +114,12 @@ namespace Conhost.UIA.Tests
Log.Comment("Move cursor to the middle-ish");
Point cursorExpected = new Point();
// H is at 5, 1. VT coords are 1-based and buffer is 0-based so adjust.
cursorExpected.Y = 5 - 1;
cursorExpected.Y = 5 - 1;
cursorExpected.X = 1 - 1;
app.UIRoot.SendKeys("H");
// Move to middle-ish from here. 10 Bs and 10 Cs should about do it.
for (int i = 0; i < 10; i++)
for (int i=0; i < 10; i++)
{
app.UIRoot.SendKeys("BC");
cursorExpected.Y++;
@@ -715,7 +715,7 @@ namespace Conhost.UIA.Tests
}
delegate char GetExpectedChar(int rowId, int colId, int height, int width);
private static void ScreenFillHelper(CmdApp app, ViewportArea area, IntPtr hConsole)
{
Log.Comment("Fill screen with junk");

View File

@@ -212,7 +212,7 @@ namespace Conhost.UIA.Tests
[TestMethod]
public void TestSelection()
{
RunTest(TestSelectionImpl);
RunTest(TestSelectionImpl);
}
private void TestSelectionImpl(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue<EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal)
@@ -295,7 +295,7 @@ namespace Conhost.UIA.Tests
[TestMethod]
public void TestLaunchAndExitChild()
{
RunTest(TestLaunchAndExitChildImpl);
RunTest(TestLaunchAndExitChildImpl);
}
private void TestLaunchAndExitChildImpl(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue<EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal)
@@ -357,11 +357,11 @@ namespace Conhost.UIA.Tests
{
RunTest(TestScrollByWheelImpl);
}
private void TestScrollByWheelImpl(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue<EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal)
{
int rowsPerScroll = app.GetRowsPerScroll();
int scrollDelta;
int scrollDelta;
// A. Scroll down.
{

View File

@@ -316,7 +316,7 @@ void ProcessCtrlEvents()
{
/*
* Status will be non-successful if a process attached to this console
* vetoes shutdown. In that case, we don't want to try to kill any more
* vetos shutdown. In that case, we don't want to try to kill any more
* processes, but we do need to make sure we continue looping so we
* can close any remaining process handles. The exception is if the
* process is inaccessible, such that we can't even open a handle for

View File

@@ -467,7 +467,7 @@ size_t InputBuffer::Prepend(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& in
try
{
_vtInputShouldSuppress = true;
auto resetVtInputSuppress = wil::scope_exit([&]() { _vtInputShouldSuppress = false; });
auto resetVtInputSupress = wil::scope_exit([&]() { _vtInputShouldSuppress = false; });
_HandleConsoleSuspensionEvents(inEvents);
if (inEvents.empty())
{

View File

@@ -123,7 +123,12 @@ COORD RenderData::GetCursorPosition() const noexcept
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto& cursor = gci.GetActiveOutputBuffer().GetTextBuffer().GetCursor();
return cursor.GetPosition();
COORD effectiveCursor = cursor.GetPosition();
if (cursor.IsDelayedEOLWrap() && cursor.GetDelayedAtPosition() == effectiveCursor)
{
effectiveCursor.X++;
}
return effectiveCursor;
}
// Method Description:
@@ -307,7 +312,7 @@ const bool RenderData::IsGridLineDrawingAllowed() noexcept
{
// Otherwise, for compatibility reasons with legacy applications that used the additional CHAR_INFO bits by accident or for their own purposes,
// we must enable grid line drawing only in a DBCS output codepage. (Line drawing historically only worked in DBCS codepages.)
// The only known instance of this is Image for Windows by TeraByte, Inc. (TeraByte Unlimited) which used the bits accidentally and for no purpose
// The only known instance of this is Image for Windows by TeraByte, Inc. (TeryByte Unlimited) which used the bits accidentally and for no purpose
// (according to the app developer) in conjunction with the Borland Turbo C cgscrn library.
return !!IsAvailableEastAsianCodePage(gci.OutputCP);
}
@@ -411,7 +416,7 @@ const COORD RenderData::GetSelectionEnd() const noexcept
// - SelectionAnchor: the initial position where the selection was started
// - SelectionRect: the rectangular region denoting a portion of the buffer that is selected
// The following is an excerpt from Selection::s_GetSelectionRects
// The following is an exerpt from Selection::s_GetSelectionRects
// if the anchor (start of select) was in the top right or bottom left of the box,
// we need to remove rectangular overlap in the middle.
// e.g.

View File

@@ -166,7 +166,7 @@ Viewport SCREEN_INFORMATION::GetBufferSize() const
// Arguments:
// - <none>
// Return Value:
// - a viewport whose height is the height of the "terminal" portion of the
// - a viewport whos height is the height of the "terminal" portion of the
// buffer in terminal scrolling mode, and is the height of the full buffer
// in normal scrolling mode.
Viewport SCREEN_INFORMATION::GetTerminalBufferSize() const
@@ -765,7 +765,7 @@ void SCREEN_INFORMATION::SetViewportSize(const COORD* const pcoordSize)
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
// If we're in terminal scrolling mode, and we're trying to set the viewport
// If we're in terminal scrolling mode, and we're rying to set the viewport
// below the logical viewport, without updating our virtual bottom
// (the logical viewport's position), dont.
// Instead move us to the bottom of the logical viewport.

Some files were not shown because too many files have changed in this diff Show More