Compare commits

...

37 Commits

Author SHA1 Message Date
Dustin L. Howett
b077abb445 Migrate spelling-0.0.21 changes from main 2020-07-14 14:16:14 -07:00
Dustin L. Howett
f9324d062d Migrate spelling-0.0.19 changes from main 2020-07-14 14:16:14 -07:00
Michael Niksa
35cae5a33d Merge more go fast fixes into spsc branch 2020-07-14 14:16:14 -07:00
Michael Niksa
a8be923688 Only check char row reference out of bounds in debug. 2020-07-14 13:05:42 -07:00
Michael Niksa
1eea75e3e7 Get cell with array notation to skip verificaton on release build. we're already checking this all over the place. 2020-07-14 12:56:13 -07:00
Michael Niksa
ad723f9671 use release/acquire over seq cst on the atomic flag in throttledfunc 2020-07-14 12:03:48 -07:00
Michael Niksa
4db39a6f55 Don't use checked lookups when we already know we're in bounds for AttrRow 2020-07-14 12:03:33 -07:00
Michael Niksa
d7255fdbef Dump this try/catch block. Everything is noexcept except for the failfast which is legitimate so don't waste time setting up and tearing down that whole fence. 2020-07-14 11:40:54 -07:00
Michael Niksa
23aef43c81 commit attr runs less frequently. accumulate lengths. 2020-07-14 11:33:19 -07:00
Michael Niksa
8c1a45f6f0 The check for RowByOffset in bounds is quite expensive on massive output. Drop it. Use the array notation so it will still work in debug mode for development but not on release. We are already restricting the row all over the place on input/output so this should be mostly redundant anyway. 2020-07-14 11:06:00 -07:00
Michael Niksa
ebe9016356 use acquire/release instead of seq_cst for single producer single consumer situation. 2020-07-14 10:16:59 -07:00
Michael Niksa
d4885149ae cache the viewport to make invalidation faster. 2020-07-14 10:04:13 -07:00
Michael Niksa
ee5c5a474b change interface to not use Clusters. Just build a string and a clustermap vector. 2020-07-13 16:59:33 -07:00
Michael Niksa
4aa62f9edd Go at the buffer a bit more directly and get some perf. 2020-07-13 16:29:11 -07:00
Michael Niksa
cc6ad0b99b Nerf the queues and go back to synchronous. 2020-07-13 15:38:19 -07:00
Michael Niksa
28e1e226ad don't waste time invalidating something that is already fully invalid. 2020-07-13 15:25:20 -07:00
Michael Niksa
6f23280011 Revert "NOT FASTER: Make the bitmap smaller and blow up the rectangles on the way out."
This reverts commit 37dab43af0.
2020-07-13 15:24:09 -07:00
Michael Niksa
559e700228 Turns out winrt::hstring doesn't like unterminated std::wstring_views. 2020-07-13 11:08:40 -07:00
Michael Niksa
93ab856ab8 Update the three spsc queues to use just plain old characters instead of whole strings. 2020-07-13 10:48:03 -07:00
Michael Niksa
bfd398f609 Merge remote-tracking branch 'lhecker/spsc' into dev/miniksa/gotta_go_fast_spsc 2020-07-13 10:10:45 -07:00
Leonard Hecker
5ab3064f07 Addressed review comments 2020-07-12 01:16:18 +02:00
Michael Niksa
2bc4749f65 Merge remote-tracking branch 'lhecker/spsc' into dev/miniksa/gotta_go_fast_spsc 2020-07-09 15:04:21 -07:00
Michael Niksa
2168ee441a don't waste time invalidating something that is already fully invalid. 2020-07-09 14:50:09 -07:00
Leonard Hecker
c3ec32539a Addressed review comments 2020-07-09 22:52:16 +02:00
Michael Niksa
40323e7c1d Revert "NOT FASTER: Make the bitmap smaller and blow up the rectangles on the way out."
This reverts commit 37dab43af0.
2020-07-08 19:07:15 -07:00
Michael Niksa
627613f649 spsc treatment for gotta_go_fast 2020-07-08 19:02:45 -07:00
Michael Niksa
60b089e7b0 take out two of the thread usages, go back to just the one for the server/_stream.cpp 2020-07-08 16:48:07 -07:00
Michael Niksa
821c39c2a3 Merge branch 'dev/miniksa/gotta_go_fast' into spscfast 2020-07-08 16:39:34 -07:00
Michael Niksa
3f23ec3465 Merge branch 'master' into spscfast 2020-07-08 16:39:26 -07:00
Michael Niksa
37dab43af0 NOT FASTER: Make the bitmap smaller and blow up the rectangles on the way out. 2020-07-08 14:00:46 -07:00
Michael Niksa
d77e55596b Also gross but it breaks the conpty connection into a read step and a write step. 2020-07-08 13:24:10 -07:00
Michael Niksa
0be1c9deef This is gross but it sort of breaks the VtEngine::_Flush off into its own thread. 2020-07-08 13:10:33 -07:00
Michael Niksa
5f9321310f cache the size viewport structure inside TextBuffer to cut down on lookup time. 2020-07-06 16:17:31 -07:00
Leonard Hecker
dad044a405 Added til::spsc, a lock-free, single-producer/-consumer FIFO queue 2020-07-06 22:04:25 +02:00
Michael Niksa
061e636163 don't copy the bitmap on the way into the tracing function. 2020-07-01 16:27:05 -07:00
Michael Niksa
8aaa93f139 Hold the buffer line string in the object so we save all the alloc/dealloc time. 2020-07-01 15:58:44 -07:00
Michael Niksa
90a24b20b8 Wow. Much fast. Very zoom. 2020-06-29 16:29:04 -07:00
72 changed files with 3268 additions and 481146 deletions

View File

@@ -1,25 +0,0 @@
<details>
<summary>
:pencil2: Contributor please read this
</summary>
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
:warning: The command is written for posix shells. You can copy the contents of each `perl` command excluding the outer `'` marks and dropping any `'"`/`"'` quotation mark pairs into a file and then run `perl file.pl` from the root of the repository to run the code. Alternatively, you can manually insert the items...
If the listed items are:
* ... **misspelled**, then please *correct* them instead of using the command.
* ... *names*, please add them to `.github/actions/spell-check/dictionary/names.txt`.
* ... APIs, you can add them to a file in `.github/actions/spell-check/dictionary/`.
* ... just things you're using, please add them to an appropriate file in `.github/actions/spell-check/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/spell-check/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>
#### :warning: Reviewers
At present, the action that triggered this message will not show its :x: in this PR unless the branch is within this repository.
Thus, you **should** make sure that this comment has been addressed before encouraging the merge bot to merge this PR.

View File

@@ -1,36 +0,0 @@
ACCEPTFILES
ACCESSDENIED
bitfield
bitfields
CLASSNOTAVAILABLE
EXPCMDFLAGS
EXPCMDSTATE
fullkbd
href
IAsync
IBox
IBind
IClass
IComparable
ICustom
IDialog
IDirect
IExplorer
IMap
IObject
IStorage
LCID
LSHIFT
NCHITTEST
NCLBUTTONDBLCLK
NCRBUTTONDBLCLK
NOAGGREGATION
NOREDIRECTIONBITMAP
oaidl
ocidl
RETURNCMD
rfind
roundf
RSHIFT
SIZENS
tmp

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,15 +0,0 @@
ACLs
backplating
DACL
DACLs
LKG
mfcribbon
microsoft
microsoftonline
osgvsowi
powerrename
powershell
SACLs
tdbuildteamid
vcruntime
visualstudio

View File

@@ -1,63 +0,0 @@
(?:^|/)dirs$
(?:^|/)go\.mod$
(?:^|/)go\.sum$
(?:^|/)package-lock\.json$
(?:^|/)sources(?:|\.dep)$
SUMS$
\.ai$
\.bmp$
\.cer$
\.class$
\.crl$
\.crt$
\.csr$
\.dll$
\.DS_Store$
\.eot$
\.eps$
\.exe$
\.gif$
\.graffle$
\.gz$
\.icns$
\.ico$
\.jar$
\.jpeg$
\.jpg$
\.key$
\.lib$
\.lock$
\.map$
\.min\..
\.mp3$
\.mp4$
\.otf$
\.pbxproj$
\.pdf$
\.pem$
\.png$
\.psd$
\.runsettings$
\.sig$
\.so$
\.svg$
\.svgz$
\.tar$
\.tgz$
\.ttf$
\.woff
\.xcf$
\.xls
\.xpm$
\.yml$
\.zip$
^consolegit2gitfilters\.json$
^dep/
^oss/
^doc/reference/UTF8-torture-test\.txt$
^src/interactivity/onecore/BgfxEngine\.
^src/renderer/wddmcon/WddmConRenderer\.
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
^\.github/actions/spell-check/
^\.gitignore$

View File

@@ -1,15 +0,0 @@
http
td
www
ecma
rapidtables
WCAG
freedesktop
ycombinator
robertelder
kovidgoyal
leonerd
fixterms
uk
winui
appshellintegration

View File

@@ -1,21 +0,0 @@
https://(?:(?:[-a-zA-Z0-9?&=]*\.|)microsoft\.com)/[-a-zA-Z0-9?&=_#\/.]*
https://aka\.ms/[-a-zA-Z0-9?&=\/_]*
https://www\.itscj\.ipsj\.or\.jp/iso-ir/[-0-9]+\.pdf
https://www\.vt100\.net/docs/[-a-zA-Z0-9#_\/.]*
https://www.w3.org/[-a-zA-Z0-9?&=\/_#]*
https://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]*
https://[a-z-]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
[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])\1{3,}\b
Base64::s_(?:En|De)code\(L"[^"]+"
VERIFY_ARE_EQUAL\(L"[^"]+"
L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\+/"

15
.github/actions/spelling/README.md vendored Normal file
View File

@@ -0,0 +1,15 @@
# check-spelling/check-spelling configuration
File | Purpose | Format | Info
-|-|-|-
[allow/*.txt](allow/) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow)
[reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject)
[excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
[patterns/*.txt](patterns/) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
[candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns)
[line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
[expect/*.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect)
[advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice)
Note: you can replace any of these files with a directory by the same name (minus the suffix)
and then include multiple files inside that directory (with that suffix) to merge multiple files together.

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

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

View File

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

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

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

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

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

View File

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

View File

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

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

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

View File

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

View File

@@ -1,43 +1,66 @@
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
@@ -45,11 +68,18 @@ Pham
Rincewind
rprichard
Schoonover
shadertoy
Shomnipotence
simioni
Somuah
sonph
sonpham
stakx
talo
thereses
Walisch
WDX
Wellons
Wirt
Wojciech
zadjii

View File

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

117
.github/actions/spelling/excludes.txt vendored Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

@@ -1,20 +0,0 @@
name: Spell checking
on:
push:
schedule:
# * is a special character in YAML so you have to quote this string
- cron: '15 * * * *'
jobs:
build:
name: Spell checking
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.0.0
with:
fetch-depth: 5
- uses: check-spelling/check-spelling@0.0.16-alpha
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
bucket: .github/actions
project: spell-check

134
.github/workflows/spelling2.yml vendored Normal file
View File

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

View File

@@ -108,7 +108,12 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
{
THROW_HR_IF(E_INVALIDARG, column >= _cchRowWidth);
const auto runPos = FindAttrIndex(column, pApplies);
return _list.at(runPos).GetAttributes();
return GetAttrByIndex(runPos);
}
TextAttribute ATTR_ROW::GetAttrByIndex(const size_t index) const
{
return _list.at(index).GetAttributes();
}
// Routine Description:
@@ -250,11 +255,11 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
if (newAttrs.size() == 1)
{
// Get the new color attribute we're trying to apply
const TextAttribute NewAttr = newAttrs.at(0).GetAttributes();
const TextAttribute NewAttr = newAttrs[0].GetAttributes();
// If the existing run was only 1 element...
// ...and the new color is the same as the old, we don't have to do anything and can exit quick.
if (_list.size() == 1 && _list.at(0).GetAttributes() == NewAttr)
if (_list.size() == 1 && _list[0].GetAttributes() == NewAttr)
{
return S_OK;
}

View File

@@ -36,6 +36,8 @@ public:
TextAttribute GetAttrByColumn(const size_t column,
size_t* const pApplies) const;
TextAttribute GetAttrByIndex(const size_t index) const;
size_t GetNumberOfRuns() const noexcept;
size_t FindAttrIndex(const size_t index,

View File

@@ -233,7 +233,9 @@ void CharRow::ClearGlyph(const size_t column)
// - Note: will throw exception if column is out of bounds
const CharRow::reference CharRow::GlyphAt(const size_t column) const
{
#ifdef DBG
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
#endif
return { const_cast<CharRow&>(*this), column };
}
@@ -246,7 +248,9 @@ const CharRow::reference CharRow::GlyphAt(const size_t column) const
// - Note: will throw exception if column is out of bounds
CharRow::reference CharRow::GlyphAt(const size_t column)
{
#ifdef DBG
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
#endif
return { *this, column };
}

View File

@@ -41,7 +41,7 @@ CharRowCellReference::operator std::wstring_view() const
// - ref to the CharRowCell
CharRowCell& CharRowCellReference::_cellData()
{
return _parent._data.at(_index);
return _parent._data[_index];
}
// Routine Description:
@@ -50,7 +50,7 @@ CharRowCell& CharRowCellReference::_cellData()
// - ref to the CharRowCell
const CharRowCell& CharRowCellReference::_cellData() const
{
return _parent._data.at(_index);
return _parent._data[_index];
}
// Routine Description:

View File

@@ -169,42 +169,33 @@ OutputCellIterator::OutputCellIterator(const std::basic_string_view<OutputCell>
// - True if the views on dereference are valid. False if it shouldn't be dereferenced.
OutputCellIterator::operator bool() const noexcept
{
try
switch (_mode)
{
switch (_mode)
{
case Mode::Loose:
case Mode::LooseTextOnly:
{
// In lieu of using start and end, this custom iterator type simply becomes bool false
// when we run out of items to iterate over.
return _pos < std::get<std::wstring_view>(_run).length();
}
case Mode::Fill:
{
if (_fillLimit > 0)
{
return _pos < _fillLimit;
}
return true;
}
case Mode::Cell:
{
return _pos < std::get<std::basic_string_view<OutputCell>>(_run).length();
}
case Mode::CharInfo:
{
return _pos < std::get<std::basic_string_view<CHAR_INFO>>(_run).length();
}
case Mode::LegacyAttr:
{
return _pos < std::get<std::wstring_view>(_run).length();
}
default:
FAIL_FAST_HR(E_NOTIMPL);
}
case Mode::Loose:
case Mode::LooseTextOnly: {
// In lieu of using start and end, this custom iterator type simply becomes bool false
// when we run out of items to iterate over.
return _pos < std::get<std::wstring_view>(_run).length();
}
case Mode::Fill: {
if (_fillLimit > 0)
{
return _pos < _fillLimit;
}
return true;
}
case Mode::Cell: {
return _pos < std::get<std::basic_string_view<OutputCell>>(_run).length();
}
case Mode::CharInfo: {
return _pos < std::get<std::basic_string_view<CHAR_INFO>>(_run).length();
}
case Mode::LegacyAttr: {
return _pos < std::get<std::wstring_view>(_run).length();
}
default:
FAIL_FAST_HR(E_NOTIMPL);
}
CATCH_FAIL_FAST();
}
// Routine Description:
@@ -218,8 +209,7 @@ OutputCellIterator& OutputCellIterator::operator++()
switch (_mode)
{
case Mode::Loose:
{
case Mode::Loose: {
if (!_TryMoveTrailing())
{
// When walking through a text sequence, we need to move forward by the number of wchar_ts consumed in the previous view
@@ -232,8 +222,7 @@ OutputCellIterator& OutputCellIterator::operator++()
}
break;
}
case Mode::LooseTextOnly:
{
case Mode::LooseTextOnly: {
if (!_TryMoveTrailing())
{
// When walking through a text sequence, we need to move forward by the number of wchar_ts consumed in the previous view
@@ -246,8 +235,7 @@ OutputCellIterator& OutputCellIterator::operator++()
}
break;
}
case Mode::Fill:
{
case Mode::Fill: {
if (!_TryMoveTrailing())
{
if (_currentView.DbcsAttr().IsTrailing())
@@ -269,8 +257,7 @@ OutputCellIterator& OutputCellIterator::operator++()
}
break;
}
case Mode::Cell:
{
case Mode::Cell: {
// Walk forward by one because cells are assumed to be in the form they needed to be
_pos++;
if (operator bool())
@@ -279,8 +266,7 @@ OutputCellIterator& OutputCellIterator::operator++()
}
break;
}
case Mode::CharInfo:
{
case Mode::CharInfo: {
// Walk forward by one because charinfos are just the legacy version of cells and prealigned to columns
_pos++;
if (operator bool())
@@ -289,8 +275,7 @@ OutputCellIterator& OutputCellIterator::operator++()
}
break;
}
case Mode::LegacyAttr:
{
case Mode::LegacyAttr: {
// Walk forward by one because color attributes apply cell by cell (no complex text information)
_pos++;
if (operator bool())

View File

@@ -160,66 +160,95 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
// If we're given a right-side column limit, use it. Otherwise, the write limit is the final column index available in the char row.
const auto finalColumnInRow = limitRight.value_or(_charRow.size() - 1);
while (it && currentIndex <= finalColumnInRow)
if (it)
{
// Fill the color if the behavior isn't set to keeping the current color.
if (it->TextAttrBehavior() != TextAttributeBehavior::Current)
// Accumulate usages of the same color so we can spend less time in InsertAttrRuns rewriting it.
auto currentColor = it->TextAttr();
size_t colorUses = 0;
size_t colorStarts = index;
while (it && currentIndex <= finalColumnInRow)
{
const TextAttributeRun attrRun{ 1, it->TextAttr() };
LOG_IF_FAILED(_attrRow.InsertAttrRuns({ &attrRun, 1 },
currentIndex,
currentIndex,
_charRow.size()));
}
// Fill the text if the behavior isn't set to saying there's only a color stored in this iterator.
if (it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly)
{
const bool fillingLastColumn = currentIndex == finalColumnInRow;
// TODO: MSFT: 19452170 - We need to ensure when writing any trailing byte that the one to the left
// is a matching leading byte. Likewise, if we're writing a leading byte, we need to make sure we still have space in this loop
// for the trailing byte coming up before writing it.
// If we're trying to fill the first cell with a trailing byte, pad it out instead by clearing it.
// Don't increment iterator. We'll advance the index and try again with this value on the next round through the loop.
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
// Fill the color if the behavior isn't set to keeping the current color.
if (it->TextAttrBehavior() != TextAttributeBehavior::Current)
{
_charRow.ClearCell(currentIndex);
// If the color of this cell is the same as the run we're currently on,
// just increment the counter.
if (currentColor == it->TextAttr())
{
++colorUses;
}
else
{
// Otherwise, commit this color into the run and save off the new one.
const TextAttributeRun run{ colorUses, currentColor };
// Now commit the new color runs into the attr row.
LOG_IF_FAILED(_attrRow.InsertAttrRuns({ &run, 1 },
index,
currentIndex - 1,
_charRow.size()));
currentColor = it->TextAttr();
colorUses = 1;
colorStarts = currentIndex;
}
}
// If we're trying to fill the last cell with a leading byte, pad it out instead by clearing it.
// Don't increment iterator. We'll exit because we couldn't write a lead at the end of a line.
else if (fillingLastColumn && it->DbcsAttr().IsLeading())
// Fill the text if the behavior isn't set to saying there's only a color stored in this iterator.
if (it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly)
{
_charRow.ClearCell(currentIndex);
_charRow.SetDoubleBytePadded(true);
const bool fillingLastColumn = currentIndex == finalColumnInRow;
// TODO: MSFT: 19452170 - We need to ensure when writing any trailing byte that the one to the left
// is a matching leading byte. Likewise, if we're writing a leading byte, we need to make sure we still have space in this loop
// for the trailing byte coming up before writing it.
// If we're trying to fill the first cell with a trailing byte, pad it out instead by clearing it.
// Don't increment iterator. We'll advance the index and try again with this value on the next round through the loop.
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
{
_charRow.ClearCell(currentIndex);
}
// If we're trying to fill the last cell with a leading byte, pad it out instead by clearing it.
// Don't increment iterator. We'll exit because we couldn't write a lead at the end of a line.
else if (fillingLastColumn && it->DbcsAttr().IsLeading())
{
_charRow.ClearCell(currentIndex);
_charRow.SetDoubleBytePadded(true);
}
// Otherwise, copy the data given and increment the iterator.
else
{
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
_charRow.GlyphAt(currentIndex) = it->Chars();
++it;
}
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
// NOTE:
// - wrap = std::nullopt --> don't change the wrap value
// - wrap = true --> we're filling cells as a steam, consider this a wrap
// - wrap = false --> we're filling cells as a block, unwrap
if (wrap.has_value() && fillingLastColumn)
{
// set wrap status on the row to parameter's value.
_charRow.SetWrapForced(wrap.value());
}
}
// Otherwise, copy the data given and increment the iterator.
else
{
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
_charRow.GlyphAt(currentIndex) = it->Chars();
++it;
}
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
// NOTE:
// - wrap = std::nullopt --> don't change the wrap value
// - wrap = true --> we're filling cells as a steam, consider this a wrap
// - wrap = false --> we're filling cells as a block, unwrap
if (wrap.has_value() && fillingLastColumn)
{
// set wrap status on the row to parameter's value.
_charRow.SetWrapForced(wrap.value());
}
}
else
{
++it;
// Move to the next cell for the next time through the loop.
++currentIndex;
}
// Move to the next cell for the next time through the loop.
++currentIndex;
// Now commit the final color into the attr row
const TextAttributeRun run{ colorUses, currentColor };
LOG_IF_FAILED(_attrRow.InsertAttrRuns({ &run, 1 },
colorStarts,
currentIndex - 1,
_charRow.size()));
}
return it;

View File

@@ -33,13 +33,16 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
_cursor{ cursorSize, *this },
_storage{},
_unicodeStorage{},
_renderTarget{ renderTarget }
_renderTarget{ renderTarget },
_size{}
{
// initialize ROWs
for (size_t i = 0; i < static_cast<size_t>(screenBufferSize.Y); ++i)
{
_storage.emplace_back(static_cast<SHORT>(i), screenBufferSize.X, _currentAttributes, this);
}
_UpdateSize();
}
// Routine Description:
@@ -78,7 +81,7 @@ const ROW& TextBuffer::GetRowByOffset(const size_t index) const
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
const size_t offsetIndex = (_firstRow + index) % totalRows;
return _storage.at(offsetIndex);
return _storage[offsetIndex];
}
// Routine Description:
@@ -94,7 +97,7 @@ ROW& TextBuffer::GetRowByOffset(const size_t index)
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
const size_t offsetIndex = (_firstRow + index) % totalRows;
return _storage.at(offsetIndex);
return _storage[offsetIndex];
}
// Routine Description:
@@ -652,9 +655,15 @@ const SHORT TextBuffer::GetFirstRowIndex() const noexcept
{
return _firstRow;
}
const Viewport TextBuffer::GetSize() const
const Viewport TextBuffer::GetSize() const noexcept
{
return Viewport::FromDimensions({ 0, 0 }, { gsl::narrow<SHORT>(_storage.at(0).size()), gsl::narrow<SHORT>(_storage.size()) });
return _size;
}
void TextBuffer::_UpdateSize()
{
_size = Viewport::FromDimensions({ 0, 0 }, { gsl::narrow<SHORT>(_storage.at(0).size()), gsl::narrow<SHORT>(_storage.size()) });
}
void TextBuffer::_SetFirstRowIndex(const SHORT FirstRowIndex) noexcept
@@ -845,6 +854,9 @@ void TextBuffer::Reset()
// Also take advantage of the row ID refresh loop to resize the rows in the X dimension
// and cleanup the UnicodeStorage characters that might fall outside the resized buffer.
_RefreshRowIDs(newSize.X);
// Update the cached size value
_UpdateSize();
}
CATCH_RETURN();

View File

@@ -110,7 +110,7 @@ public:
const SHORT GetFirstRowIndex() const noexcept;
const Microsoft::Console::Types::Viewport GetSize() const;
const Microsoft::Console::Types::Viewport GetSize() const noexcept;
void ScrollRows(const SHORT firstRow, const SHORT size, const SHORT delta);
@@ -177,6 +177,8 @@ public:
std::optional<std::reference_wrapper<PositionInformation>> positionInfo);
private:
void _UpdateSize();
Microsoft::Console::Types::Viewport _size;
std::deque<ROW> _storage;
Cursor _cursor;

View File

@@ -383,6 +383,47 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
CATCH_LOG()
static auto channelpair = til::spsc::channel<wchar_t>(16 * 1024);
static ConptyConnection* obj = nullptr;
static void terminalOutputHandlerMethod()
{
std::vector<wchar_t> buf(16 * 1024, L'\0');
while (true)
{
// OK. So this is going to go all the way down to a winrt::hstring.
// "But!", you say, "isn't a `std::wstring_view` like we're creating for the passing
// (to avoid a copy) not guaranteed to be null terminated?"
// Well, yes, you're right. However, the `winrt::hstring(std::wstring_view)` constuctor
// doesn't seem to care about that. It has the full intent of just taking the data pointer
// and embedding it inside itself as the hstring reference (instead of copying).
// That's a good thing. Except that hstrings have to be null terminated.
// So it contains a beautiful check for str[size] != 0 to ensure that the view you gave it
// was null terminated and it just flat out `std::terminate`s you if it's not. (You know
// instead of doing something like copying it or throwing or documenting any of these facts
// in either the header, the constructor, or on MSDN.)
auto [count, ok] = channelpair.second.pop_n(til::spsc::block_initially, buf.data(), buf.size() - 1);
if (!ok)
{
break;
}
// Make sure the end of it is null or risk the wrath of `winrt::hstring` when we hit the
// WinRT event callback on the way out of this module (see above).
buf[count] = '\0';
obj->_DoOutputThreadWork(std::wstring_view{ buf.data(), count });
}
}
static std::thread th(terminalOutputHandlerMethod);
void ConptyConnection::_DoOutputThreadWork(std::wstring_view str)
{
_TerminalOutputHandlers(str);
}
DWORD ConptyConnection::_OutputThread()
{
// Keep us alive until the output thread terminates; the destructor
@@ -445,7 +486,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
// Pass the output to our registered event handlers
_TerminalOutputHandlers(_u16Str);
if (!obj)
{
obj = this;
}
channelpair.first.push_n(_u16Str.data(), _u16Str.size());
//_TerminalOutputHandlers(_u16Str);
}
return 0;

View File

@@ -66,6 +66,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
std::array<char, 4096> _buffer;
DWORD _OutputThread();
public:
void _DoOutputThreadWork(std::wstring_view str);
};
}

View File

@@ -29,7 +29,7 @@ ThrottledFunc<>::ThrottledFunc(ThrottledFunc::Func func, TimeSpan delay, CoreDis
// - <none>
void ThrottledFunc<>::Run()
{
if (_isRunPending.test_and_set())
if (_isRunPending.test_and_set(std::memory_order_acquire))
{
// already pending
return;
@@ -44,7 +44,7 @@ void ThrottledFunc<>::Run()
if (auto self{ weakThis.lock() })
{
timer.Stop();
self->_isRunPending.clear();
self->_isRunPending.clear(std::memory_order_release);
self->_func();
}
});

View File

@@ -1062,10 +1062,10 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// - S_OK if successful.
// - S_OK if we need to wait (check if ppWaiter is not nullptr).
// - Or a suitable HRESULT code for math/string/memory failures.
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleAImpl(IConsoleOutputObject& context,
const std::string_view buffer,
size_t& read,
std::unique_ptr<IWaitRoutine>& waiter) noexcept
[[nodiscard]] HRESULT WriteConsoleAImplForReals(IConsoleOutputObject& context,
const std::string_view buffer,
size_t& read,
std::unique_ptr<IWaitRoutine>& waiter) noexcept
{
try
{
@@ -1238,6 +1238,50 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
CATCH_RETURN();
}
static auto channelpair = til::spsc::channel<char>(16* 1024);
static IConsoleOutputObject* obj = nullptr;
static void ioWriteConsoleMethod()
{
size_t read = 0;
std::unique_ptr<IWaitRoutine> wait;
std::vector<char> buf(16 * 1024, '\0');
while (true)
{
auto [count, ok] = channelpair.second.pop_n(til::spsc::block_initially, buf.data(), buf.size());
if (!ok)
{
break;
}
LOG_IF_FAILED(WriteConsoleAImplForReals(*obj, std::string_view{ buf.data(), count }, read, wait));
}
}
static std::thread th(ioWriteConsoleMethod);
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleAImpl(IConsoleOutputObject& context,
const std::string_view buffer,
size_t& read,
std::unique_ptr<IWaitRoutine>& waiter) noexcept
try
{
read = buffer.size();
waiter.reset();
if (!obj)
{
obj = &context;
}
channelpair.first.push_n(buffer.data(), buffer.size());
return S_OK;
//return WriteConsoleAImplForReals(context, buffer, read, waiter);
}
CATCH_RETURN()
// Routine Description:
// - Writes Unicode formatted data into the given console output object.
// - NOTE: This may be blocked for various console states and will return a wait context pointer if necessary.

View File

@@ -15,6 +15,7 @@
#include "til/rectangle.h"
#include "til/bitmap.h"
#include "til/u8u16convert.h"
#include "til/spsc.h"
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{

603
src/inc/til/spsc.h Normal file
View File

@@ -0,0 +1,603 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
// til::spsc::details::arc requires std::atomic<size_type>::wait() and ::notify_one() and at the time of writing no
// STL supports these. Since both Windows and Linux offer a Futex implementation we can easily implement this though.
// On other platforms we fall back to using a std::condition_variable.
#if __cpp_lib_atomic_wait >= 201907
#define _TIL_SPSC_DETAIL_POSITION_IMPL_NATIVE 1
#elif defined(_WIN32_WINNT) && _WIN32_WINNT >= _WIN32_WINNT_WIN8
#define _TIL_SPSC_DETAIL_POSITION_IMPL_WIN 1
#elif __linux__
#define _TIL_SPSC_DETAIL_POSITION_IMPL_LINUX 1
#else
#define _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK 1
#endif
namespace til::spsc // Terminal Implementation Library. Also: "Today I Learned"
{
using size_type = uint32_t;
namespace details
{
static constexpr size_type position_mask = std::numeric_limits<size_type>::max() >> 2u; // 0b00111....
static constexpr size_type revolution_flag = 1u << (std::numeric_limits<size_type>::digits - 2u); // 0b01000....
static constexpr size_type drop_flag = 1u << (std::numeric_limits<size_type>::digits - 1u); // 0b10000....
struct block_initially_policy
{
using _spsc_policy = int;
static constexpr bool _block_forever = false;
};
struct block_forever_policy
{
using _spsc_policy = int;
static constexpr bool _block_forever = true;
};
template<typename WaitPolicy>
using enable_if_wait_policy_t = typename std::remove_reference_t<WaitPolicy>::_spsc_policy;
#if _TIL_SPSC_DETAIL_POSITION_IMPL_NATIVE
using atomic_size_type = std::atomic<size_type>;
#else
struct atomic_size_type
{
size_type load(std::memory_order order) const noexcept
{
return _value.load(order);
}
void store(size_type desired, std::memory_order order) noexcept
{
#if _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK
// We must use a lock here to prevent us from modifying the value
// in between wait() reading the value and the thread being suspended.
std::lock_guard<std::mutex> lock{ _m };
#endif
_value.store(desired, order);
}
void wait(size_type old, [[maybe_unused]] std::memory_order order) noexcept
{
#if _TIL_SPSC_DETAIL_POSITION_IMPL_WIN
WaitOnAddress(const_cast<std::atomic<size_type>*>(&_value), &old, sizeof(_value), INFINITE);
#elif _TIL_SPSC_DETAIL_POSITION_IMPL_LINUX
futex(FUTEX_WAIT_PRIVATE, old);
#elif _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK
std::unique_lock<std::mutex> lock{ _m };
_cv.wait(lock, [&]() { return _value.load(order) != old; });
#endif
}
void notify_one() noexcept
{
#if _TIL_SPSC_DETAIL_POSITION_IMPL_WIN
WakeByAddressSingle(&_value);
#elif _TIL_SPSC_DETAIL_POSITION_IMPL_LINUX
futex(FUTEX_WAKE_PRIVATE, 1);
#elif _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK
_cv.notify_one();
#endif
}
private:
#if _TIL_SPSC_DETAIL_POSITION_IMPL_LINUX
inline void futex(int futex_op, size_type val) const noexcept
{
// See: https://man7.org/linux/man-pages/man2/futex.2.html
static_assert(sizeof(std::atomic<size_type>) == 4);
syscall(SYS_futex, &_value, futex_op, val, nullptr, nullptr, 0);
}
#endif
std::atomic<size_type> _value{ 0 };
#if _TIL_SPSC_DETAIL_POSITION_IMPL_FALLBACK
private:
std::mutex _m;
std::condition_variable _cv;
#endif
};
#endif
template<typename T>
inline T* alloc_raw_memory(size_t size)
{
constexpr auto alignment = alignof(T);
if constexpr (alignment <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
return static_cast<T*>(::operator new(size));
}
else
{
return static_cast<T*>(::operator new(size, std::align_val_t(alignment)));
}
}
template<typename T>
inline void free_raw_memory(T* ptr) noexcept
{
constexpr auto alignment = alignof(T);
if constexpr (alignment <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
::operator delete(ptr);
}
else
{
::operator delete(ptr, std::align_val_t(alignment));
}
}
struct acquisition
{
size_type begin;
size_type end;
size_type next;
bool alive;
constexpr acquisition(size_type begin, size_type end, size_type next, bool alive) :
begin(begin),
end(end),
next(next),
alive(alive)
{
}
};
// arc follows the classic spsc design and manages a ring buffer with two positions: _producer and _consumer.
// They contain the position the producer / consumer will next write to / read from respectively.
// The producer's writable range is [_producer, _consumer) and the consumer's readable is [_consumer, _producer).
// As these are symmetric, the logic for acquiring and releasing ranges is the same for both sides.
// The producer will acquire() and release() ranges with its own position as "mine" and the consumer's position as "theirs".
// The arguments are correspondingly flipped for the consumer.
//
// While the producer is logically always ahead of the consumer, due to the underlying
// buffer being a ring buffer, the producer's position might be smaller than the consumer's
// position, if both are calculated modulo the buffer's capacity, as we're doing here.
// As such the logic range [_producer, _consumer) might actually be the two ranges
// [_producer, _capacity) & [0, _consumer)
// if _producer > _consumer, modulo _capacity, since the range wraps around the end of the ring buffer.
//
// The producer cannot write more values ahead of the consumer than the buffer's capacity.
// Inversely the consumer must wait until the producer has written at least one value ahead.
// In order to implement the first requirement the positions will flip their "revolution_flag" bit each other
// revolution around the ring buffer. If the positions are identical, except for their "revolution_flag"
// value it signals to the producer that it's ahead by one "revolution", or capacity-many values.
// The second requirement can similarly be detected if the two positions are identical including this bit.
template<typename T>
struct arc
{
explicit arc(size_type capacity) noexcept :
_data(alloc_raw_memory<T>(size_t(capacity) * sizeof(T))),
_capacity(capacity)
{
}
~arc()
{
auto beg = _consumer.load(std::memory_order_acquire);
auto end = _producer.load(std::memory_order_acquire);
auto differentRevolution = ((beg ^ end) & revolution_flag) != 0;
beg &= position_mask;
end &= position_mask;
// The producer position will always be ahead of the consumer, but since we're dealing
// with a ring buffer the producer may be wrapped around the end of the buffer.
// We thus need to deal with 3 potential cases:
// * No valid data.
// If both positions including their revolution bits are identical.
// * Valid data in the middle of the ring buffer.
// If _producer > _consumer.
// * Valid data at both ends of the ring buffer.
// If the revolution bits differ, even if the positions are otherwise identical,
// which they might be if the channel contains exactly as many values as its capacity.
if (end > beg)
{
std::destroy(_data + beg, _data + end);
}
else if (differentRevolution)
{
std::destroy(_data, _data + end);
std::destroy(_data + beg, _data + _capacity);
}
free_raw_memory(_data);
}
void drop_producer()
{
drop(_producer);
}
void drop_consumer()
{
drop(_consumer);
}
acquisition producer_acquire(size_type slots, bool blocking) noexcept
{
return acquire(_producer, _consumer, revolution_flag, slots, blocking);
}
void producer_release(acquisition acquisition) noexcept
{
release(_producer, acquisition);
}
acquisition consumer_acquire(size_type slots, bool blocking) noexcept
{
return acquire(_consumer, _producer, 0, slots, blocking);
}
void consumer_release(acquisition acquisition) noexcept
{
release(_consumer, acquisition);
}
T* data() const noexcept
{
return _data;
}
private:
void drop(atomic_size_type& mine)
{
// Signal the other side we're dropped. See acquire() for the handling of the drop_flag.
// We don't need to use release ordering like release() does as each call to
// any of the producer/consumer methods already results in a call to release().
// Another release ordered write can't possibly synchronize any more data anyways at this point.
const auto myPos = mine.load(std::memory_order_relaxed);
mine.store(myPos | drop_flag, std::memory_order_relaxed);
mine.notify_one();
// The first time SPSCBase is dropped (destroyed) we'll set
// the flag to true and get false, causing us to return early.
// Only the second time we'll get true.
// --> The contents are only deleted when both sides have been dropped.
if (_eitherSideDropped.exchange(true, std::memory_order_relaxed))
{
delete this;
}
}
// NOTE: waitMask MUST be either 0 (consumer) or revolution_flag (producer).
acquisition acquire(atomic_size_type& mine, atomic_size_type& theirs, size_type waitMask, size_type slots, bool blocking) noexcept
{
size_type myPos = mine.load(std::memory_order_relaxed);
size_type theirPos;
while (true)
{
// This acquire read synchronizes with the release write in release().
theirPos = theirs.load(std::memory_order_acquire);
if ((myPos ^ theirPos) != waitMask)
{
break;
}
if (!blocking)
{
return {
0,
0,
0,
true,
};
}
theirs.wait(theirPos, std::memory_order_relaxed);
}
// If the other side's position contains a drop flag, as a X -> we need to...
// * producer -> stop immediately
// Only the producer's waitMask is != 0.
// * consumer -> finish consuming all values and then stop
// We're finished if the only difference between our
// and the other side's position is the drop flag.
if ((theirPos & drop_flag) != 0 && (waitMask != 0 || (myPos ^ theirPos) == drop_flag))
{
return {
0,
0,
0,
false,
};
}
auto begin = myPos & position_mask;
auto end = theirPos & position_mask;
// [begin, end) is the writable/readable range for the producer/consumer.
// The following detects whether we'd be wrapping around the end of the ring buffer
// and splits the range into the first half [mine, _capacity).
// If acquire() is called again it'll return [0, theirs).
end = end > begin ? end : _capacity;
// Of course we also need to ensure to not return more than we've been asked for.
end = std::min(end, begin + slots);
// "next" will contain the value that's stored into "mine" when release() is called.
// It's basically the same as "end", but with the revolution flag spliced in.
// If we acquired the range [mine, _capacity) "end" will equal _capacity
// and thus wrap around the ring buffer. The next value for "mine" is thus the
// position zero | the flipped "revolution" (and 0 | x == x).
auto revolution = myPos & revolution_flag;
auto next = end != _capacity ? end | revolution : revolution ^ revolution_flag;
return {
begin,
end,
next,
true,
};
}
void release(atomic_size_type& mine, acquisition acquisition) noexcept
{
// This release write synchronizes with the acquire read in acquire().
mine.store(acquisition.next, std::memory_order_release);
mine.notify_one();
}
T* const _data;
const size_type _capacity;
std::atomic<bool> _eitherSideDropped{ false };
atomic_size_type _producer;
atomic_size_type _consumer;
};
inline void validate_size(size_t v)
{
if (v > static_cast<size_t>(position_mask))
{
throw std::overflow_error{ "size too large for spsc" };
}
}
}
inline constexpr details::block_initially_policy block_initially{};
inline constexpr details::block_forever_policy block_forever{};
template<typename T>
struct producer
{
explicit producer(details::arc<T>* arc) noexcept :
_arc(arc) {}
producer<T>(const producer<T>&) = delete;
producer<T>& operator=(const producer<T>&) = delete;
producer(producer<T>&& other) noexcept
{
drop();
_arc = std::exchange(other._arc, nullptr);
}
producer<T>& operator=(producer<T>&& other) noexcept
{
drop();
_arc = std::exchange(other._arc, nullptr);
}
~producer()
{
drop();
}
// emplace constructs an item in-place at the end of the queue.
// It returns true, if the item was successfully placed within the queue.
// The return value will be false, if the consumer is gone.
template<typename... Args>
bool emplace(Args&&... args) const
{
auto acquisition = _arc->producer_acquire(1, true);
if (!acquisition.end)
{
return false;
}
auto data = _arc->data();
auto begin = data + acquisition.begin;
new (begin) T(std::forward<Args>(args)...);
_arc->producer_release(acquisition);
return true;
}
template<typename InputIt>
std::pair<size_t, bool> push(InputIt first, InputIt last) const
{
return push_n(block_forever, first, std::distance(first, last));
}
// move_n moves count items from the input iterator in into the queue.
// The resulting iterator is returned as the first pair field.
// The second pair field will be false if the consumer is gone.
template<typename WaitPolicy, typename InputIt, details::enable_if_wait_policy_t<WaitPolicy> = 0>
std::pair<size_t, bool> push(WaitPolicy&& policy, InputIt first, InputIt last) const
{
return push_n(std::forward<WaitPolicy>(policy), first, std::distance(first, last));
}
template<typename InputIt>
std::pair<size_t, bool> push_n(InputIt first, size_t count) const
{
return push_n(block_forever, first, count);
}
// move_n moves count items from the input iterator in into the queue.
// The resulting iterator is returned as the first pair field.
// The second pair field will be false if the consumer is gone.
template<typename WaitPolicy, typename InputIt, details::enable_if_wait_policy_t<WaitPolicy> = 0>
std::pair<size_t, bool> push_n(WaitPolicy&&, InputIt first, size_t count) const
{
details::validate_size(count);
const auto data = _arc->data();
auto remaining = static_cast<size_type>(count);
auto blocking = true;
auto ok = true;
while (remaining != 0)
{
auto acquisition = _arc->producer_acquire(remaining, blocking);
if (!acquisition.end)
{
ok = acquisition.alive;
break;
}
const auto begin = data + acquisition.begin;
const auto got = acquisition.end - acquisition.begin;
std::uninitialized_copy_n(first, got, begin);
first += got;
remaining -= got;
_arc->producer_release(acquisition);
if constexpr (!std::remove_reference_t<WaitPolicy>::_block_forever)
{
blocking = false;
}
}
return { count - remaining, ok };
}
private:
void drop()
{
if (_arc)
{
_arc->drop_producer();
}
}
details::arc<T>* _arc = nullptr;
};
template<typename T>
struct consumer
{
explicit consumer(details::arc<T>* arc) noexcept :
_arc(arc) {}
consumer<T>(const consumer<T>&) = delete;
consumer<T>& operator=(const consumer<T>&) = delete;
consumer(consumer<T>&& other) noexcept
{
drop();
_arc = std::exchange(other._arc, nullptr);
}
consumer<T>& operator=(consumer<T>&& other) noexcept
{
drop();
_arc = std::exchange(other._arc, nullptr);
}
~consumer()
{
drop();
}
// pop returns the next item in the queue, or std::nullopt
// if no items are available and the producer is gone.
// The call blocks until either of these events occur.
std::optional<T> pop() const
{
auto acquisition = _arc->consumer_acquire(1, true);
if (!acquisition.end)
{
return std::nullopt;
}
auto data = _arc->data();
auto begin = data + acquisition.begin;
auto item = std::move(*begin);
std::destroy_at(begin);
_arc->consumer_release(acquisition);
return item;
}
template<typename OutputIt>
std::pair<size_t, bool> pop_n(OutputIt first, size_t count) const
{
return pop_n(block_forever, first, count);
}
// pop_n writes up to count items into the given output iterator out.
// The resulting iterator is returned as the first pair field.
// The second pair field will be false if no items are available and the producer is gone.
template<typename WaitPolicy, typename OutputIt, details::enable_if_wait_policy_t<WaitPolicy> = 0>
std::pair<size_t, bool> pop_n(WaitPolicy&&, OutputIt first, size_t count) const
{
details::validate_size(count);
const auto data = _arc->data();
auto remaining = static_cast<size_type>(count);
auto blocking = true;
auto ok = true;
while (remaining != 0)
{
auto acquisition = _arc->consumer_acquire(remaining, blocking);
if (!acquisition.end)
{
ok = acquisition.alive;
break;
}
auto beg = data + acquisition.begin;
auto end = data + acquisition.end;
auto got = acquisition.end - acquisition.begin;
first = std::move(beg, end, first);
std::destroy(beg, end);
remaining -= got;
_arc->consumer_release(acquisition);
if constexpr (!std::remove_reference_t<WaitPolicy>::_block_forever)
{
blocking = false;
}
}
return { count - remaining, ok };
}
private:
void drop()
{
if (_arc)
{
_arc->drop_consumer();
}
}
details::arc<T>* _arc = nullptr;
};
// channel returns a bounded, lock-free, single-producer, single-consumer
// FIFO queue ("channel") with the given maximum capacity.
template<typename T>
std::pair<producer<T>, consumer<T>> channel(uint32_t capacity)
{
if (capacity == 0)
{
throw std::invalid_argument{ "invalid capacity" };
}
const auto arc = new details::arc<T>(capacity);
return { std::piecewise_construct, std::forward_as_tuple(arc), std::forward_as_tuple(arc) };
}
}

View File

@@ -29,10 +29,10 @@ Renderer::Renderer(IRenderData* pData,
_pData(pData),
_pThread{ std::move(thread) },
_destructing{ false },
_clusterBuffer{}
_text{},
_clusterMap{},
_viewport{Viewport::Empty()}
{
_srViewportPrevious = { 0 };
for (size_t i = 0; i < cEngines; i++)
{
IRenderEngine* engine = rgpEngines[i];
@@ -208,15 +208,16 @@ void Renderer::TriggerSystemRedraw(const RECT* const prcDirtyClient)
// - <none>
void Renderer::TriggerRedraw(const Viewport& region)
{
Viewport view = _pData->GetViewport();
Viewport view = _viewport;
SMALL_RECT srUpdateRegion = region.ToExclusive();
if (view.TrimToViewport(&srUpdateRegion))
{
view.ConvertToOrigin(&srUpdateRegion);
std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) {
for (auto pEngine : _rgpEngines)
{
LOG_IF_FAILED(pEngine->Invalidate(&srUpdateRegion));
});
}
_NotifyPaintFrame();
}
@@ -357,7 +358,7 @@ void Renderer::TriggerSelection()
// - True if something changed and we scrolled. False otherwise.
bool Renderer::_CheckViewportAndScroll()
{
SMALL_RECT const srOldViewport = _srViewportPrevious;
SMALL_RECT const srOldViewport = _viewport.ToInclusive();
SMALL_RECT const srNewViewport = _pData->GetViewport().ToInclusive();
COORD coordDelta;
@@ -369,13 +370,13 @@ bool Renderer::_CheckViewportAndScroll()
LOG_IF_FAILED(engine->UpdateViewport(srNewViewport));
}
_srViewportPrevious = srNewViewport;
_viewport = Viewport::FromInclusive(srNewViewport);
// If we're keeping some buffers between calls, let them know about the viewport size
// so they can prepare the buffers for changes to either preallocate memory at once
// (instead of growing naturally) or shrink down to reduce usage as appropriate.
const size_t lineLength = gsl::narrow_cast<size_t>(til::rectangle{ srNewViewport }.width());
til::manage_vector(_clusterBuffer, lineLength, _shrinkThreshold);
til::manage_vector(_clusterMap, lineLength, _shrinkThreshold);
if (coordDelta.X != 0 || coordDelta.Y != 0)
{
@@ -654,7 +655,8 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
const auto screenLine = Viewport::Offset(bufferLine, -view.Origin());
// Retrieve the cell information iterator limited to just this line we want to redraw.
auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine);
auto& r = buffer.GetRowByOffset(bufferLine.Origin().Y);
//auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine);
// Calculate if two things are true:
// 1. this row wrapped
@@ -664,27 +666,30 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
(bufferLine.RightExclusive() == buffer.GetSize().Width());
// Ask the helper to paint through this specific line.
_PaintBufferOutputHelper(pEngine, it, screenLine.Origin(), lineWrapped);
_PaintBufferOutputHelper(pEngine, r, bufferLine.RightExclusive(), screenLine.Origin(), lineWrapped);
}
}
}
}
static bool _IsAllSpaces(const std::wstring_view v)
{
// first non-space char is not found (is npos)
return v.find_first_not_of(L" ") == decltype(v)::npos;
}
//static bool _IsAllSpaces(const std::wstring_view v)
//{
// // first non-space char is not found (is npos)
// return v.find_first_not_of(L" ") == decltype(v)::npos;
//}
void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
TextBufferCellIterator it,
const ROW& r,
const SHORT limitRight,
const COORD target,
const bool lineWrapped)
{
auto globalInvert{ _pData->IsScreenReversed() };
/*auto globalInvert{ _pData->IsScreenReversed() };*/
SHORT pos = target.X;
// If we have valid data, let's figure out how to draw it.
if (it)
if (pos < limitRight)
{
// TODO: MSFT: 20961091 - This is a perf issue. Instead of rebuilding this and allocing memory to hold the reinterpretation,
// we should have an iterator/view adapter for the rendering.
@@ -693,13 +698,18 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
size_t cols = 0;
// Retrieve the first color.
auto color = it->TextAttr();
size_t colorApplies = 0;
const auto& charRow = r.GetCharRow();
const auto& attrRow = r.GetAttrRow();
auto colorIdx = attrRow.FindAttrIndex(pos, &colorApplies);
auto color = attrRow.GetAttrByIndex(colorIdx);
// And hold the point where we should start drawing.
auto screenPoint = target;
// This outer loop will continue until we reach the end of the text we are trying to draw.
while (it)
while (pos < limitRight)
{
// Hold onto the current run color right here for the length of the outer loop.
// We'll be changing the persistent one as we run through the inner loops to detect
@@ -716,11 +726,12 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
// Hold onto the start of this run iterator and the target location where we started
// in case we need to do some special work to paint the line drawing characters.
const auto currentRunItStart = it;
const auto currentRunItStart = pos;
const auto currentRunTargetStart = screenPoint;
// Ensure that our cluster vector is clear.
_clusterBuffer.clear();
_text.clear();
_clusterMap.clear();
// Reset our flag to know when we're in the special circumstance
// of attempting to draw only the right-half of a two-column character
@@ -734,25 +745,32 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
// When the color changes, it will save the new color off and break.
do
{
if (color != it->TextAttr())
if (colorApplies <= 0)
{
auto newAttr{ it->TextAttr() };
// foreground doesn't matter for runs of spaces (!)
// if we trick it . . . we call Paint far fewer times for cmatrix
if (!_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, globalInvert))
{
color = newAttr;
break; // vend this run
}
colorIdx = attrRow.FindAttrIndex(pos, &colorApplies);
color = attrRow.GetAttrByIndex(colorIdx);
break;
//auto newAttr{ it->TextAttr() };
//// foreground doesn't matter for runs of spaces (!)
//// if we trick it . . . we call Paint far fewer times for cmatrix
//if (!_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, globalInvert))
//{
// color = newAttr;
// break; // vend this run
//}
}
// Walk through the text data and turn it into rendering clusters.
// Keep the columnCount as we go to improve performance over digging it out of the vector at the end.
size_t columnCount = 0;
const auto dbcsAttr = charRow.DbcsAttrAt(pos);
const auto chars = (std::wstring_view)charRow.GlyphAt(pos);
// If we're on the first cluster to be added and it's marked as "trailing"
// (a.k.a. the right half of a two column character), then we need some special handling.
if (_clusterBuffer.empty() && it->DbcsAttr().IsTrailing())
if (_text.empty() && dbcsAttr.IsTrailing())
{
// If we have room to move to the left to start drawing...
if (screenPoint.X > 0)
@@ -762,8 +780,12 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
// And tell the next function to trim off the left half of it.
trimLeft = true;
// And add one to the number of columns we expect it to take as we insert it.
columnCount = it->Columns() + 1;
_clusterBuffer.emplace_back(it->Chars(), columnCount);
columnCount = 2;
//columnCount = it->Columns() + 1;
_text.append(chars);
_clusterMap.push_back(2);
_clusterMap.push_back(0);
//_clusterBuffer.emplace_back(chars, columnCount);
}
else
{
@@ -775,8 +797,14 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
// Otherwise if it's not a special case, just insert it as is.
else
{
columnCount = it->Columns();
_clusterBuffer.emplace_back(it->Chars(), columnCount);
columnCount = dbcsAttr.IsLeading() ? 2 : 1;
_text.append(chars);
_clusterMap.push_back((UINT16)columnCount);
if (columnCount > 1)
{
_clusterMap.push_back(0);
}
//_clusterBuffer.emplace_back(chars, columnCount);
}
if (columnCount > 1)
@@ -785,13 +813,15 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
}
// Advance the cluster and column counts.
it += columnCount > 0 ? columnCount : 1; // prevent infinite loop for no visible columns
const auto delta = (SHORT)(columnCount > 0 ? columnCount : 1); // prevent infinite loop for no visible columns
pos += delta;
colorApplies -= delta;
cols += columnCount;
} while (it);
} while (pos < limitRight);
// Do the painting.
THROW_IF_FAILED(pEngine->PaintBufferLine({ _clusterBuffer.data(), _clusterBuffer.size() }, screenPoint, trimLeft, lineWrapped));
THROW_IF_FAILED(pEngine->PaintBufferLine(_text, { _clusterMap.data(), _clusterMap.size() }, screenPoint, trimLeft, lineWrapped));
// If we're allowed to do grid drawing, draw that now too (since it will be coupled with the color data)
// We're only allowed to draw the grid lines under certain circumstances.
@@ -816,7 +846,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
// Do that in the future if some WPR trace points you to this spot as super bad.
for (auto colsPainted = 0u; colsPainted < cols; ++colsPainted, ++lineIt, ++lineTarget.X)
{
auto lines = lineIt->TextAttr();
auto lines = attrRow.GetAttrByColumn(lineIt);
_PaintBufferOutputGridLineHelper(pEngine, lines, 1, lineTarget);
}
}
@@ -1013,7 +1043,7 @@ void Renderer::_PaintOverlay(IRenderEngine& engine,
auto it = overlay.buffer.GetCellLineDataAt(source);
_PaintBufferOutputHelper(&engine, it, target, false);
//_PaintBufferOutputHelper(&engine, it, target, false);
}
}
}

View File

@@ -99,10 +99,16 @@ namespace Microsoft::Console::Render
void _PaintBufferOutput(_In_ IRenderEngine* const pEngine);
void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
TextBufferCellIterator it,
const ROW& r,
const SHORT limitRight,
const COORD target,
const bool lineWrapped);
//void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
// TextBufferCellIterator it,
// const COORD target,
// const bool lineWrapped);
static IRenderEngine::GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept;
void _PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngine,
@@ -120,10 +126,12 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine);
SMALL_RECT _srViewportPrevious;
Microsoft::Console::Types::Viewport _viewport;
static constexpr float _shrinkThreshold = 0.8f;
std::vector<Cluster> _clusterBuffer;
std::wstring _text;
std::vector<UINT16> _clusterMap;
//std::vector<Cluster> _clusterBuffer;
std::vector<SMALL_RECT> _GetSelectionRects() const;
void _ScrollPreviousSelection(const til::point delta);

View File

@@ -162,17 +162,17 @@ DWORD WINAPI RenderThread::_ThreadProc()
{
WaitForSingleObject(_hPaintEnabledEvent, INFINITE);
if (!_fNextFrameRequested.exchange(false))
if (!_fNextFrameRequested.exchange(false, std::memory_order_acq_rel))
{
// <--
// If `NotifyPaint` is called at this point, then it will not
// set the event because `_fWaiting` is not `true` yet so we have
// to check again below.
_fWaiting.store(true);
_fWaiting.store(true, std::memory_order_release);
// check again now (see comment above)
if (!_fNextFrameRequested.exchange(false))
if (!_fNextFrameRequested.exchange(false, std::memory_order_acq_rel))
{
// Wait until a next frame is requested.
WaitForSingleObject(_hEvent, INFINITE);
@@ -193,7 +193,7 @@ DWORD WINAPI RenderThread::_ThreadProc()
// expensive operation, we should reset the event to not render
// again if nothing changed.
_fWaiting.store(false);
_fWaiting.store(false, std::memory_order_release);
// see comment above
ResetEvent(_hEvent);
@@ -218,13 +218,13 @@ DWORD WINAPI RenderThread::_ThreadProc()
void RenderThread::NotifyPaint()
{
if (_fWaiting.load())
if (_fWaiting.load(std::memory_order_acquire))
{
SetEvent(_hEvent);
}
else
{
_fNextFrameRequested.store(true);
_fNextFrameRequested.store(true, std::memory_order_release);
}
}

View File

@@ -78,25 +78,27 @@ CATCH_RETURN()
// - clusters - From the backing buffer, the text to be displayed clustered by the columns it should consume.
// Return Value:
// - S_OK or suitable memory management issue.
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::AppendClusters(const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters)
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::AppendClusters(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap)
try
{
_textClusterColumns.reserve(_textClusterColumns.size() + clusters.size());
_text.append(text);
_textClusterColumns.insert(_textClusterColumns.end(), clusterMap.cbegin(), clusterMap.cend());
for (const auto& cluster : clusters)
{
const auto cols = gsl::narrow<UINT16>(cluster.GetColumns());
const auto text = cluster.GetText();
//for (const auto& cluster : clusters)
//{
// const auto cols = gsl::narrow<UINT16>(cluster.GetColumns());
// const auto text = cluster.GetText();
// Push back the number of columns for this bit of text.
_textClusterColumns.push_back(cols);
// // Push back the number of columns for this bit of text.
// _textClusterColumns.push_back(cols);
// If there is more than one text character here, push 0s for the rest of the columns
// of the text run.
_textClusterColumns.resize(_textClusterColumns.size() + base::ClampSub(text.size(), 1u), gsl::narrow_cast<UINT16>(0u));
// // If there is more than one text character here, push 0s for the rest of the columns
// // of the text run.
// _textClusterColumns.resize(_textClusterColumns.size() + base::ClampSub(text.size(), 1u), gsl::narrow_cast<UINT16>(0u));
_text += text;
}
// _text += text;
//}
return S_OK;
}

View File

@@ -27,7 +27,8 @@ namespace Microsoft::Console::Render
size_t const width,
IBoxDrawingEffect* const boxEffect);
[[nodiscard]] HRESULT STDMETHODCALLTYPE AppendClusters(const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters);
[[nodiscard]] HRESULT STDMETHODCALLTYPE AppendClusters(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap);
[[nodiscard]] HRESULT STDMETHODCALLTYPE Reset() noexcept;

View File

@@ -68,6 +68,7 @@ DxEngine::DxEngine() :
_invalidateFullRows{ true },
_invalidMap{},
_invalidScroll{},
_allInvalid{ false },
_firstFrame{ true },
_presentParams{ 0 },
_presentReady{ false },
@@ -847,6 +848,11 @@ void DxEngine::_InvalidateRectangle(const til::rectangle& rc)
_invalidMap.set(invalidate);
}
bool DxEngine::_IsAllInvalid() const noexcept
{
return std::llabs(_invalidScroll.y()) >= _invalidMap.size().height();
}
// Routine Description:
// - Invalidates a rectangle described in characters
// Arguments:
@@ -858,7 +864,10 @@ try
{
RETURN_HR_IF_NULL(E_INVALIDARG, psrRegion);
_InvalidateRectangle(Viewport::FromExclusive(*psrRegion).ToInclusive());
if (!_allInvalid)
{
_InvalidateRectangle(Viewport::FromExclusive(*psrRegion).ToInclusive());
}
return S_OK;
}
@@ -875,7 +884,10 @@ try
{
RETURN_HR_IF_NULL(E_INVALIDARG, pcoordCursor);
_InvalidateRectangle(til::rectangle{ *pcoordCursor, til::size{ 1, 1 } });
if (!_allInvalid)
{
/*_InvalidateRectangle(til::rectangle{ *pcoordCursor, til::size{ 1, 1 } });*/
}
return S_OK;
}
@@ -892,9 +904,12 @@ try
{
RETURN_HR_IF_NULL(E_INVALIDARG, prcDirtyClient);
// Dirty client is in pixels. Use divide specialization against glyph factor to make conversion
// to cells.
_InvalidateRectangle(til::rectangle{ *prcDirtyClient }.scale_down(_glyphCell));
if (!_allInvalid)
{
// Dirty client is in pixels. Use divide specialization against glyph factor to make conversion
// to cells.
_InvalidateRectangle(til::rectangle{ *prcDirtyClient }.scale_down(_glyphCell));
}
return S_OK;
}
@@ -908,9 +923,12 @@ CATCH_RETURN();
// - S_OK
[[nodiscard]] HRESULT DxEngine::InvalidateSelection(const std::vector<SMALL_RECT>& rectangles) noexcept
{
for (const auto& rect : rectangles)
if (!_allInvalid)
{
RETURN_IF_FAILED(Invalidate(&rect));
for (const auto& rect : rectangles)
{
RETURN_IF_FAILED(Invalidate(&rect));
}
}
return S_OK;
}
@@ -930,11 +948,15 @@ try
const til::point deltaCells{ *pcoordDelta };
if (deltaCells != til::point{ 0, 0 })
if (!_allInvalid)
{
// Shift the contents of the map and fill in revealed area.
_invalidMap.translate(deltaCells, true);
_invalidScroll += deltaCells;
if (deltaCells != til::point{ 0, 0 })
{
// Shift the contents of the map and fill in revealed area.
_invalidMap.translate(deltaCells, true);
_invalidScroll += deltaCells;
_allInvalid = _IsAllInvalid();
}
}
return S_OK;
@@ -951,6 +973,7 @@ CATCH_RETURN();
try
{
_invalidMap.set_all();
_allInvalid = true;
// Since everything is invalidated here, mark this as a "first frame", so
// that we won't use incremental drawing on it. The caller of this intended
@@ -1209,6 +1232,7 @@ try
}
_invalidMap.reset_all();
_allInvalid = false;
_invalidScroll = {};
@@ -1419,7 +1443,8 @@ CATCH_RETURN()
// - fTrimLeft - Whether or not to trim off the left half of a double wide character
// Return Value:
// - S_OK or relevant DirectX error
[[nodiscard]] HRESULT DxEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT DxEngine::PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
COORD const coord,
const bool /*trimLeft*/,
const bool /*lineWrapped*/) noexcept
@@ -1430,7 +1455,7 @@ try
// Create the text layout
RETURN_IF_FAILED(_customLayout->Reset());
RETURN_IF_FAILED(_customLayout->AppendClusters(clusters));
RETURN_IF_FAILED(_customLayout->AppendClusters(text, clusterMap));
// Layout then render the text
RETURN_IF_FAILED(_customLayout->Draw(_drawingContext.get(), _customRenderer.Get(), origin.x, origin.y));
@@ -1819,10 +1844,12 @@ try
{
RETURN_HR_IF_NULL(E_INVALIDARG, pResult);
const Cluster cluster(glyph, 0); // columns don't matter, we're doing analysis not layout.
//const Cluster cluster(glyph, 0); // columns don't matter, we're doing analysis not layout.
UINT16 col = 0;
RETURN_IF_FAILED(_customLayout->Reset());
RETURN_IF_FAILED(_customLayout->AppendClusters({ &cluster, 1 }));
RETURN_IF_FAILED(_customLayout->AppendClusters(glyph, { &col, 1 }));
UINT32 columns = 0;
RETURN_IF_FAILED(_customLayout->GetColumns(&columns));

View File

@@ -86,7 +86,8 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
COORD const coord,
bool const fTrimLeft,
const bool lineWrapped) noexcept override;
@@ -157,6 +158,7 @@ namespace Microsoft::Console::Render
bool _invalidateFullRows;
til::bitmap _invalidMap;
til::point _invalidScroll;
bool _allInvalid;
bool _presentReady;
std::vector<RECT> _presentDirty;
@@ -271,6 +273,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] til::size _GetClientSize() const;
void _InvalidateRectangle(const til::rectangle& rc);
bool _IsAllInvalid() const noexcept;
[[nodiscard]] D2D1_COLOR_F _ColorFFromColorRef(const COLORREF color) noexcept;

View File

@@ -42,7 +42,8 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT ScrollFrame() noexcept override;
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord,
const bool trimLeft,
const bool lineWrapped) noexcept override;

View File

@@ -284,14 +284,15 @@ using namespace Microsoft::Console::Render;
// See: Win7: 390673, 447839 and then superseded by http://osgvsowi/638274 when FE/non-FE rendering condensed.
//#define CONSOLE_EXTTEXTOUT_FLAGS ETO_OPAQUE | ETO_CLIPPED
//#define MAX_POLY_LINES 80
[[nodiscard]] HRESULT GdiEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT GdiEngine::PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord,
const bool trimLeft,
const bool /*lineWrapped*/) noexcept
{
try
{
const auto cchLine = clusters.size();
const auto cchLine = text.size();
// Exit early if there are no lines to draw.
RETURN_HR_IF(S_OK, 0 == cchLine);
@@ -316,12 +317,13 @@ using namespace Microsoft::Console::Render;
// Convert data from clusters into the text array and the widths array.
for (size_t i = 0; i < cchLine; i++)
{
const auto& cluster = clusters.at(i);
//const auto& cluster = clusters.at(i);
// Our GDI renderer hasn't and isn't going to handle things above U+FFFF or sequences.
// So replace anything complicated with a replacement character for drawing purposes.
pwsPoly[i] = cluster.GetTextAsSingle();
rgdxPoly[i] = gsl::narrow<int>(cluster.GetColumns()) * coordFontSize.X;
pwsPoly[i] = text[i];
//cluster.GetTextAsSingle();
rgdxPoly[i] = gsl::narrow<int>(clusterMap[i]) * coordFontSize.X;
cchCharWidths += rgdxPoly[i];
}
@@ -365,7 +367,7 @@ using namespace Microsoft::Console::Render;
}
pPolyTextLine->lpstr = pwsPoly.release();
pPolyTextLine->n = gsl::narrow<UINT>(clusters.size());
pPolyTextLine->n = gsl::narrow<UINT>(text.size());
pPolyTextLine->x = ptDraw.x;
pPolyTextLine->y = ptDraw.y;
pPolyTextLine->uiFlags = ETO_OPAQUE | ETO_CLIPPED;

View File

@@ -71,7 +71,8 @@ namespace Microsoft::Console::Render
[[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] virtual HRESULT PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord,
const bool fTrimLeft,
const bool lineWrapped) noexcept = 0;

View File

@@ -305,7 +305,8 @@ CATCH_RETURN();
// - fTrimLeft - Whether or not to trim off the left half of a double wide character
// Return Value:
// - S_FALSE
[[nodiscard]] HRESULT UiaEngine::PaintBufferLine(std::basic_string_view<Cluster> const /*clusters*/,
[[nodiscard]] HRESULT UiaEngine::PaintBufferLine(std::wstring_view /*text*/,
std::basic_string_view<UINT16> /*clusterMap*/,
COORD const /*coord*/,
const bool /*trimLeft*/,
const bool /*lineWrapped*/) noexcept

View File

@@ -51,7 +51,8 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override;
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
COORD const coord,
bool const fTrimLeft,
const bool lineWrapped) noexcept override;

View File

@@ -498,14 +498,15 @@ CATCH_RETURN();
// will be false.
// Return Value:
// - S_OK or suitable HRESULT error from writing pipe.
[[nodiscard]] HRESULT XtermEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT XtermEngine::PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord,
const bool /*trimLeft*/,
const bool lineWrapped) noexcept
{
return _fUseAsciiOnly ?
VtEngine::_PaintAsciiBufferLine(clusters, coord) :
VtEngine::_PaintUtf8BufferLine(clusters, coord, lineWrapped);
VtEngine::_PaintAsciiBufferLine(text, clusterMap, coord) :
VtEngine::_PaintUtf8BufferLine(text, clusterMap, coord, lineWrapped);
}
// Method Description:

View File

@@ -41,7 +41,8 @@ namespace Microsoft::Console::Render
[[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes,
const gsl::not_null<IRenderData*> pData,
const bool isSettingDefaultBrushes) noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord,
const bool trimLeft,
const bool lineWrapped) noexcept override;

View File

@@ -3,6 +3,9 @@
#include "precomp.h"
#include <algorithm>
#include <numeric>
#include "vtrenderer.hpp"
#include "../../inc/conattrs.hpp"
#include "../../types/inc/convert.hpp"
@@ -125,12 +128,13 @@ using namespace Microsoft::Console::Types;
// will be false.
// Return Value:
// - S_OK or suitable HRESULT error from writing pipe.
[[nodiscard]] HRESULT VtEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT VtEngine::PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord,
const bool /*trimLeft*/,
const bool /*lineWrapped*/) noexcept
{
return VtEngine::_PaintAsciiBufferLine(clusters, coord);
return VtEngine::_PaintAsciiBufferLine(text, clusterMap, coord);
}
// Method Description:
@@ -327,27 +331,28 @@ using namespace Microsoft::Console::Types;
// - coord - character coordinate target to render within viewport
// Return Value:
// - S_OK or suitable HRESULT error from writing pipe.
[[nodiscard]] HRESULT VtEngine::_PaintAsciiBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT VtEngine::_PaintAsciiBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord) noexcept
{
try
{
RETURN_IF_FAILED(_MoveCursor(coord));
std::wstring wstr;
wstr.reserve(clusters.size());
/*_bufferLine.clear();*/
//_bufferLine.reserve(clusters.size());
short totalWidth = 0;
/*short totalWidth = 0;
for (const auto& cluster : clusters)
{
wstr.append(cluster.GetText());
_bufferLine.append(cluster.GetText());
RETURN_IF_FAILED(ShortAdd(totalWidth, gsl::narrow<short>(cluster.GetColumns()), &totalWidth));
}
}*/
RETURN_IF_FAILED(VtEngine::_WriteTerminalAscii(wstr));
RETURN_IF_FAILED(VtEngine::_WriteTerminalAscii(text));
// Update our internal tracker of the cursor's position
_lastText.X += totalWidth;
_lastText.X += (SHORT)std::accumulate(clusterMap.begin(), clusterMap.end(), 0);
return S_OK;
}
@@ -362,7 +367,8 @@ using namespace Microsoft::Console::Types;
// - coord - character coordinate target to render within viewport
// Return Value:
// - S_OK or suitable HRESULT error from writing pipe.
[[nodiscard]] HRESULT VtEngine::_PaintUtf8BufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT VtEngine::_PaintUtf8BufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord,
const bool lineWrapped) noexcept
{
@@ -371,21 +377,21 @@ using namespace Microsoft::Console::Types;
return S_OK;
}
std::wstring unclusteredString;
unclusteredString.reserve(clusters.size());
short totalWidth = 0;
for (const auto& cluster : clusters)
/*_bufferLine.clear();
_bufferLine.reserve(clusters.size());*/
short totalWidth = (SHORT)std::accumulate(clusterMap.begin(), clusterMap.end(), 0);
/*for (const auto& cluster : clusters)
{
unclusteredString.append(cluster.GetText());
_bufferLine.append(cluster.GetText());
RETURN_IF_FAILED(ShortAdd(totalWidth, static_cast<short>(cluster.GetColumns()), &totalWidth));
}
const size_t cchLine = unclusteredString.size();
}*/
const size_t cchLine = text.size();
bool foundNonspace = false;
size_t lastNonSpace = 0;
for (size_t i = 0; i < cchLine; i++)
{
if (unclusteredString.at(i) != L'\x20')
if (text.at(i) != L'\x20')
{
lastNonSpace = i;
foundNonspace = true;
@@ -479,8 +485,7 @@ using namespace Microsoft::Console::Types;
RETURN_IF_FAILED(_MoveCursor(coord));
// Write the actual text string
std::wstring wstr = std::wstring(unclusteredString.data(), cchActual);
RETURN_IF_FAILED(VtEngine::_WriteTerminalUtf8(wstr));
RETURN_IF_FAILED(VtEngine::_WriteTerminalUtf8({ text.data(), cchActual }));
// GH#4415, GH#5181
// If the renderer told us that this was a wrapped line, then mark

View File

@@ -98,6 +98,28 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
CATCH_RETURN();
}
static auto channelpair = til::spsc::channel<char>(16* 1024);
static HANDLE obj = nullptr;
static void vtRenderWriteMethod()
{
std::vector<char> buf(16 * 1024, '\0');
while (true)
{
auto [count, ok] = channelpair.second.pop_n(til::spsc::block_initially, buf.data(), buf.size());
if (!ok)
{
break;
}
WriteFile(obj, buf.data(), static_cast<DWORD>(count), nullptr, nullptr);
}
}
static std::thread th(vtRenderWriteMethod);
[[nodiscard]] HRESULT VtEngine::_Flush() noexcept
{
#ifdef UNIT_TESTING
@@ -108,7 +130,16 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
}
#endif
if (!_pipeBroken)
if (!obj)
{
obj = _hFile.get();
}
channelpair.first.push_n(_buffer.data(), _buffer.size());
_buffer.clear();
return S_OK;
/* if (!_pipeBroken)
{
bool fSuccess = !!WriteFile(_hFile.get(), _buffer.data(), static_cast<DWORD>(_buffer.size()), nullptr, nullptr);
_buffer.clear();
@@ -122,9 +153,7 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
}
return _exitResult;
}
}
return S_OK;
}*/
}
// Method Description:

View File

@@ -145,7 +145,7 @@ void RenderTracing::TraceInvalidateScroll(const til::point scroll) const
}
void RenderTracing::TraceStartPaint(const bool quickReturn,
const til::bitmap invalidMap,
const til::bitmap& invalidMap,
const til::rectangle lastViewport,
const til::point scrollDelt,
const bool cursorMoved,

View File

@@ -39,7 +39,7 @@ namespace Microsoft::Console::VirtualTerminal
void TraceTriggerCircling(const bool newFrame) const;
void TraceInvalidateScroll(const til::point scroll) const;
void TraceStartPaint(const bool quickReturn,
const til::bitmap invalidMap,
const til::bitmap& invalidMap,
const til::rectangle lastViewport,
const til::point scrollDelta,
const bool cursorMoved,

View File

@@ -61,7 +61,8 @@ namespace Microsoft::Console::Render
[[nodiscard]] virtual HRESULT ScrollFrame() noexcept = 0;
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] virtual HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] virtual HRESULT PaintBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord,
const bool trimLeft,
const bool lineWrapped) noexcept override;
@@ -204,11 +205,16 @@ namespace Microsoft::Console::Render
bool _WillWriteSingleChar() const;
[[nodiscard]] HRESULT _PaintUtf8BufferLine(std::basic_string_view<Cluster> const clusters,
// buffer space for these two functions to build their lines
// so they don't have to alloc/free in a tight loop
//std::wstring _bufferLine;
[[nodiscard]] HRESULT _PaintUtf8BufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord,
const bool lineWrapped) noexcept;
[[nodiscard]] HRESULT _PaintAsciiBufferLine(std::basic_string_view<Cluster> const clusters,
[[nodiscard]] HRESULT _PaintAsciiBufferLine(std::wstring_view text,
std::basic_string_view<UINT16> clusterMap,
const COORD coord) noexcept;
[[nodiscard]] HRESULT _WriteTerminalUtf8(const std::wstring_view str) noexcept;

View File

@@ -0,0 +1,194 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
struct drop_indicator
{
explicit drop_indicator(int& counter) noexcept :
_counter(&counter) {}
drop_indicator(const drop_indicator&) = delete;
drop_indicator& operator=(const drop_indicator&) = delete;
drop_indicator(drop_indicator&& other) noexcept
{
_counter = std::exchange(other._counter, nullptr);
}
drop_indicator& operator=(drop_indicator&& other) noexcept
{
_counter = std::exchange(other._counter, nullptr);
}
~drop_indicator()
{
if (_counter)
{
++*_counter;
}
}
private:
int* _counter = nullptr;
};
template<typename T>
void drop(T&& val)
{
auto _ = std::move(val);
}
class SPSCTests
{
BEGIN_TEST_CLASS(SPSCTests)
TEST_CLASS_PROPERTY(L"TestTimeout", L"0:0:10") // 10s timeout
END_TEST_CLASS()
TEST_METHOD(DropEmptyTest);
TEST_METHOD(DropSameRevolutionTest);
TEST_METHOD(DropDifferentRevolutionTest);
TEST_METHOD(IntegrationTest);
};
void SPSCTests::DropEmptyTest()
{
auto [tx, rx] = til::spsc::channel<drop_indicator>(5);
int counter = 0;
for (int i = 0; i < 5; ++i)
{
tx.emplace(counter);
}
VERIFY_ARE_EQUAL(counter, 0);
for (int i = 0; i < 5; ++i)
{
rx.pop();
}
VERIFY_ARE_EQUAL(counter, 5);
for (int i = 0; i < 3; ++i)
{
tx.emplace(counter);
}
VERIFY_ARE_EQUAL(counter, 5);
drop(tx);
VERIFY_ARE_EQUAL(counter, 5);
for (int i = 0; i < 3; ++i)
{
rx.pop();
}
VERIFY_ARE_EQUAL(counter, 8);
drop(rx);
VERIFY_ARE_EQUAL(counter, 8);
}
void SPSCTests::DropSameRevolutionTest()
{
auto [tx, rx] = til::spsc::channel<drop_indicator>(5);
int counter = 0;
for (int i = 0; i < 5; ++i)
{
tx.emplace(counter);
}
VERIFY_ARE_EQUAL(counter, 0);
drop(tx);
VERIFY_ARE_EQUAL(counter, 0);
for (int i = 0; i < 3; ++i)
{
rx.pop();
}
VERIFY_ARE_EQUAL(counter, 3);
drop(rx);
VERIFY_ARE_EQUAL(counter, 5);
}
void SPSCTests::DropDifferentRevolutionTest()
{
auto [tx, rx] = til::spsc::channel<drop_indicator>(5);
int counter = 0;
for (int i = 0; i < 4; ++i)
{
tx.emplace(counter);
}
VERIFY_ARE_EQUAL(counter, 0);
for (int i = 0; i < 3; ++i)
{
rx.pop();
}
VERIFY_ARE_EQUAL(counter, 3);
for (int i = 0; i < 4; ++i)
{
tx.emplace(counter);
}
VERIFY_ARE_EQUAL(counter, 3);
// At this point we emplace()d 8 items and pop()ed 3 in a channel with a capacity of 5.
// Both producer and consumer positions will be 3 and only differ in their revolution flag.
// This ensures that the arc<T> destructor works even if
drop(tx);
VERIFY_ARE_EQUAL(counter, 3);
drop(rx);
VERIFY_ARE_EQUAL(counter, 8);
}
void SPSCTests::IntegrationTest()
{
auto [tx, rx] = til::spsc::channel<int>(7);
std::thread t([tx = std::move(tx)]() {
std::array<int, 11> buffer{};
std::generate(buffer.begin(), buffer.end(), [v = 0]() mutable { return v++; });
for (int i = 0; i < 37; ++i)
{
tx.emplace(i);
}
for (int i = 0; i < 3; ++i)
{
tx.push(buffer.begin(), buffer.end());
}
});
std::array<int, 11> buffer{};
for (int i = 0; i < 3; ++i)
{
rx.pop_n(buffer.data(), buffer.size());
for (int j = 0; j < 11; ++j)
{
VERIFY_ARE_EQUAL(i * 11 + j, buffer[j]);
}
}
for (int i = 33; i < 37; ++i)
{
auto actual = rx.pop();
VERIFY_ARE_EQUAL(i, actual);
}
for (int i = 0; i < 33; ++i)
{
auto expected = i % 11;
auto actual = rx.pop();
VERIFY_ARE_EQUAL(expected, actual);
}
t.join();
}

View File

@@ -22,6 +22,7 @@
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="SPSCTests.cpp" />
<ClCompile Include="u8u16convertTests.cpp" />
</ItemGroup>
<ItemGroup>

View File

@@ -15,6 +15,7 @@
<ClCompile Include="OperatorTests.cpp" />
<ClCompile Include="MathTests.cpp" />
<ClCompile Include="BaseTests.cpp" />
<ClCompile Include="SPSCTests.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\precomp.h" />