Compare commits

...

22 Commits

Author SHA1 Message Date
Dustin L. Howett
37138fcaaf Migrate spelling-0.0.21 changes from main 2021-07-30 12:34:39 -05:00
Dustin Howett
5bdb5e6caa Break a huge amount of the console
Writing CHAR_INFO doesn't work
RowImage is half-baked
Row.hpp contains eight reimplementations of the same slgotihm
move shit around
RowImage.split is fucking madness
look for TODO(DH) everywhere

however, termbench performance doubles for non-colored text.
2021-07-30 12:41:30 -05:00
Dustin Howett
d2f0f50651 Make the writers fun templates 2021-07-29 16:54:26 -05:00
Dustin Howett
271132edb4 Remove the OCI sources 2021-07-29 16:44:47 -05:00
Dustin Howett
7c5b39a041 OCI-DEP: Remove some more OCI 2021-07-29 13:25:12 -05:00
Dustin Howett
34035509a9 OCI-DEP: Remove all Attribute (Region/Count) fills 2021-07-28 15:40:32 -05:00
Dustin Howett
a13f6237ff WriteStringCOntiguous + WriteCharsLegacy[Measurement] 2021-07-28 15:12:55 -05:00
Dustin Howett
97702a1d9d WCL-HAX: fix wrap-repeat and buffer reuse bug 2021-07-27 18:48:26 -05:00
Dustin Howett
d4cdbc9b91 Update for Chester's TBCI change 2021-07-27 18:48:01 -05:00
Dustin Howett
596a8155ca Move Attr, Cwid, Data into nested type. Unify Damage interface to support returning damage for one column (replace off+size with min/max damage) and for multiple cols. 2021-07-27 18:46:44 -05:00
Dustin Howett
58ad0a34fc remove dead code, non-rle code, and de-tuplise 2021-07-27 13:53:51 -05:00
Dustin Howett
8f4c4f4916 and nuke the local copy of rle 2021-07-27 13:53:51 -05:00
Dustin Howett
8133f2856d tidy 2021-07-27 13:53:51 -05:00
Dustin Howett
f64660ac2f new cleaner variable names 2021-07-27 13:53:51 -05:00
Dustin Howett
b1a981daa9 port to Leonard's RLE 2021-07-27 13:53:48 -05:00
Dustin L. Howett
7d8df11ede wishlist 2021-07-27 13:51:49 -05:00
Dustin L. Howett
89fde46a94 Clean up the RLE defines 2021-07-27 13:51:49 -05:00
Dustin L. Howett
00af538278 HAX: rle-based row 2021-07-27 13:51:46 -05:00
Dustin Howett
227ce8ff20 beef prevention 2021-07-27 13:42:54 -05:00
Dustin Howett
99d9bac51d Kill GlyphAt (writable and const) 2021-07-27 13:42:52 -05:00
Dustin Howett
97a2dc6878 Rewrite Reflow 2021-07-27 13:41:53 -05:00
Dustin Howett
abf66b2ff8 NO TESTS: Add OututCellIterator that takes TextBufferCellIterator 2021-07-27 13:38:05 -05:00
61 changed files with 2290 additions and 3772 deletions

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

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

View File

@@ -1,4 +1,4 @@
<!-- markdownlint-disable MD033 MD041 -->
<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details>
<summary>
:pencil2: Contributor please read this
@@ -6,7 +6,7 @@
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
:warning: The command is written for posix shells. You can copy the contents of each `perl` command excluding the outer `'` marks and dropping any `'"`/`"'` quotation mark pairs into a file and then run `perl file.pl` from the root of the repository to run the code. Alternatively, you can manually insert the items...
:warning: The command is written for posix shells. If it doesn't work for you, you can manually _add_ (one word per line) / _remove_ items to `expect.txt` and the `excludes.txt` files.
If the listed items are:
@@ -20,31 +20,29 @@ See the `README.md` in each directory for more information.
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
<details><summary>:clamp: If you see a bunch of garbage</summary>
If it relates to a ...
<details><summary>well-formed pattern</summary>
<details><summary>If the flagged items are :exploding_head: false positives</summary>
See if there's a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it.
If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).
If not, try writing one and adding it to a `patterns/{file}.txt`.
Please add a file path to the `excludes.txt` file matching the containing file.
Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
Note that patterns can't match multiline strings.
</details>
<details><summary>binary-ish string</summary>
Please add a file path to the `excludes.txt` file instead of just accepting the garbage.
File paths are Perl 5 Regular Expressions - you can [test](
File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).
</details>
* well-formed pattern.
If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
try adding it to the `patterns.txt` file.
Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
Note that patterns can't match multiline strings.
</details>
</details>

View File

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

View File

@@ -1,28 +1,44 @@
ACCEPTFILES
ACCESSDENIED
acl
aclapi
alignas
alignof
APPLYTOSUBMENUS
appxrecipe
bitfield
bitfields
BUILDBRANCH
BUILDMSG
BUILDNUMBER
BYCOMMAND
BYPOSITION
charconv
CLASSNOTAVAILABLE
CLOSEAPP
cmdletbinding
COLORPROPERTY
colspan
COMDLG
commandlinetoargv
comparand
cstdint
CXICON
CYICON
Dacl
dataobject
dcomp
DERR
dlldata
DNE
DONTADDTORECENT
DWMSBT
DWMWA
DWMWA
DWORDLONG
endfor
ENDSESSION
enumset
environstrings
EXPCMDFLAGS
EXPCMDSTATE
@@ -35,12 +51,16 @@ fullkbd
futex
GETDESKWALLPAPER
GETHIGHCONTRAST
GETMOUSEHOVERTIME
Hashtable
HIGHCONTRASTON
HIGHCONTRASTW
hotkeys
href
hrgn
HTCLOSE
hwinsta
HWINSTA
IActivation
IApp
IAppearance
@@ -57,18 +77,22 @@ IDirect
IExplorer
IFACEMETHOD
IFile
IGraphics
IInheritable
IMap
IMonarch
IObject
iosfwd
IPackage
IPeasant
ISetup
isspace
IStorage
istream
IStringable
ITab
ITaskbar
itow
IUri
IVirtual
KEYSELECT
@@ -77,13 +101,27 @@ llabs
llu
localtime
lround
Lsa
lsass
LSHIFT
LTGRAY
MAINWINDOW
memchr
memicmp
MENUCOMMAND
MENUDATA
MENUINFO
MENUITEMINFOW
mmeapi
MOUSELEAVE
mov
mptt
msappx
MULTIPLEUSE
NCHITTEST
NCLBUTTONDBLCLK
NCMOUSELEAVE
NCMOUSEMOVE
NCRBUTTONDBLCLK
NIF
NIN
@@ -93,6 +131,7 @@ NOCHANGEDIR
NOPROGRESS
NOREDIRECTIONBITMAP
NOREPEAT
NOTIFYBYPOS
NOTIFYICON
NOTIFYICONDATA
ntprivapi
@@ -100,26 +139,36 @@ oaidl
ocidl
ODR
offsetof
ofstream
onefuzz
osver
OSVERSIONINFOEXW
otms
OUTLINETEXTMETRICW
overridable
PACL
PAGESCROLL
PATINVERT
PEXPLICIT
PICKFOLDERS
pmr
ptstr
QUERYENDSESSION
rcx
REGCLS
RETURNCMD
rfind
ROOTOWNER
roundf
RSHIFT
SACL
schandle
semver
serializer
SETVERSION
SHELLEXECUTEINFOW
shobjidl
SHOWHIDE
SHOWMINIMIZED
SHOWTIP
SINGLEUSE
@@ -133,28 +182,44 @@ 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
@@ -171,6 +236,7 @@ xlocmes
xlocmon
xlocnum
xloctime
XMax
xmemory
XParse
xpath
@@ -179,3 +245,4 @@ xstring
xtree
xutility
YIcon
YMax

View File

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

View File

@@ -1,5 +1,6 @@
ACLs
ADMINS
advapi
altform
altforms
appendwttlogging
@@ -15,8 +16,10 @@ CPLs
cpptools
cppvsdbg
CPRs
cryptbase
DACL
DACLs
defaultlib
diffs
disposables
dotnetfeed
@@ -24,15 +27,22 @@ DTDs
DWINRT
enablewttlogging
Intelli
IVisual
libucrt
libucrtd
LKG
LOCKFILE
Lxss
mfcribbon
microsoft
microsoftonline
MSAA
msixbundle
MSVC
MSVCP
muxc
netcore
Onefuzz
osgvsowi
PFILETIME
pgc
@@ -43,6 +53,7 @@ powershell
propkey
pscustomobject
QWORD
regedit
robocopy
SACLs
sdkddkver
@@ -56,6 +67,8 @@ systemroot
taskkill
tasklist
tdbuildteamid
ucrt
ucrtd
unvirtualized
VCRT
vcruntime

View File

@@ -1,14 +1,18 @@
Anup
austdi
arkthur
Ballmer
bhoj
Bhojwani
Bluloco
carlos
dhowett
Diviness
dsafa
duhowett
DXP
ekg
eryksun
ethanschoonover
Firefox
Gatta
@@ -20,6 +24,7 @@ Hernan
Howett
Illhardt
iquilezles
italo
jantari
jerrysh
Kaiyu
@@ -31,8 +36,11 @@ Kourosh
kowalczyk
leonmsft
Lepilleur
lhecker
lukesampson
Macbook
Manandhar
masserano
mbadolato
Mehrain
menger
@@ -52,6 +60,7 @@ oldnewthing
opengl
osgwiki
pabhojwa
panos
paulcam
pauldotknopf
PGP
@@ -60,12 +69,17 @@ Rincewind
rprichard
Schoonover
shadertoy
Shomnipotence
simioni
Somuah
sonph
sonpham
stakx
talo
thereses
Walisch
WDX
Wellons
Wirt
Wojciech
zadjii

View File

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

View File

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

View File

@@ -5,26 +5,19 @@ AAAAAABBBBBBCCC
AAAAABBBBBBCCC
abcd
abcd
abcde
abcdef
ABCDEFG
ABCDEFGH
ABCDEFGHIJ
abcdefghijk
ABCDEFGHIJKLMNO
abcdefghijklmnop
ABCDEFGHIJKLMNOPQRST
abcdefghijklmnopqrstuvwxyz
ABCG
ABE
abf
BBBBB
BBBBBBBB
BBBBBBBBBBBBBBDDDD
BBBBBCCC
BBBBCCCCC
BBGGRR
CCE
EFG
EFGh
QQQQQQQQQQABCDEFGHIJ
@@ -33,7 +26,6 @@ QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
qrstuvwxyz
qwerty
QWERTYUIOP
qwertyuiopasdfg
YYYYYYYDDDDDDDDDDD
ZAAZZ

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,6 @@
http
www
ecma
rapidtables
WCAG
freedesktop
ycombinator
robertelder
kovidgoyal
leonerd
fixterms
winui
appshellintegration
cppreference
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

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

View File

@@ -1,22 +1,12 @@
^attache$
^attacher$
^attachers$
^spae$
^spaebook$
^spaecraft$
^spaed$
^spaedom$
^spaeing$
^spaeings$
^spae-man$
^spaeman$
^spaer$
^Spaerobee$
^spaes$
^spaewife$
^spaewoman$
^spaework$
^spaewright$
^wether$
^wethers$
^wetherteg$
benefitting
occurences?
^dependan.*
^oer$
Sorce
^[Ss]pae.*
^untill$
^untilling$
^wether.*

View File

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

View File

@@ -31,6 +31,8 @@ public:
using const_iterator = rle_vector::const_iterator;
ATTR_ROW(uint16_t width, TextAttribute attr);
ATTR_ROW(rle_vector&& v) :
_data(std::move(v)) {}
~ATTR_ROW() = default;
@@ -57,11 +59,11 @@ public:
friend bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept;
friend class ROW;
rle_vector _data;
private:
void Reset(const TextAttribute attr);
rle_vector _data;
#ifdef UNIT_TESTING
friend class CommonState;
#endif

View File

@@ -1,281 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "CharRow.hpp"
#include "unicode.hpp"
#include "Row.hpp"
// Routine Description:
// - constructor
// Arguments:
// - rowWidth - the size (in wchar_t) of the char and attribute rows
// - pParent - the parent ROW
// Return Value:
// - instantiated object
// Note: will through if unable to allocate char/attribute buffers
#pragma warning(push)
#pragma warning(disable : 26447) // small_vector's constructor says it can throw but it should not given how we use it. This suppresses this error for the AuditMode build.
CharRow::CharRow(size_t rowWidth, ROW* const pParent) noexcept :
_data(rowWidth, value_type()),
_pParent{ FAIL_FAST_IF_NULL(pParent) }
{
}
#pragma warning(pop)
// Routine Description:
// - gets the size of the row, in glyph cells
// Arguments:
// - <none>
// Return Value:
// - the size of the row
size_t CharRow::size() const noexcept
{
return _data.size();
}
// Routine Description:
// - Sets all properties of the CharRowBase to default values
// Arguments:
// - sRowWidth - The width of the row.
// Return Value:
// - <none>
void CharRow::Reset() noexcept
{
for (auto& cell : _data)
{
cell.Reset();
}
}
// Routine Description:
// - resizes the width of the CharRowBase
// Arguments:
// - newSize - the new width of the character and attributes rows
// Return Value:
// - S_OK on success, otherwise relevant error code
[[nodiscard]] HRESULT CharRow::Resize(const size_t newSize) noexcept
{
try
{
const value_type insertVals;
_data.resize(newSize, insertVals);
}
CATCH_RETURN();
return S_OK;
}
typename CharRow::iterator CharRow::begin() noexcept
{
return _data.begin();
}
typename CharRow::const_iterator CharRow::cbegin() const noexcept
{
return _data.cbegin();
}
typename CharRow::iterator CharRow::end() noexcept
{
return _data.end();
}
typename CharRow::const_iterator CharRow::cend() const noexcept
{
return _data.cend();
}
// Routine Description:
// - Inspects the current internal string to find the left edge of it
// Arguments:
// - <none>
// Return Value:
// - The calculated left boundary of the internal string.
size_t CharRow::MeasureLeft() const noexcept
{
const_iterator it = _data.cbegin();
while (it != _data.cend() && it->IsSpace())
{
++it;
}
return it - _data.cbegin();
}
// Routine Description:
// - Inspects the current internal string to find the right edge of it
// Arguments:
// - <none>
// Return Value:
// - The calculated right boundary of the internal string.
size_t CharRow::MeasureRight() const
{
const_reverse_iterator it = _data.crbegin();
while (it != _data.crend() && it->IsSpace())
{
++it;
}
return _data.crend() - it;
}
void CharRow::ClearCell(const size_t column)
{
_data.at(column).Reset();
}
// Routine Description:
// - Tells you whether or not this row contains any valid text.
// Arguments:
// - <none>
// Return Value:
// - True if there is valid text in this row. False otherwise.
bool CharRow::ContainsText() const noexcept
{
for (const value_type& cell : _data)
{
if (!cell.IsSpace())
{
return true;
}
}
return false;
}
// Routine Description:
// - gets the attribute at the specified column
// Arguments:
// - column - the column to get the attribute for
// Return Value:
// - the attribute
// Note: will throw exception if column is out of bounds
const DbcsAttribute& CharRow::DbcsAttrAt(const size_t column) const
{
return _data.at(column).DbcsAttr();
}
// Routine Description:
// - gets the attribute at the specified column
// Arguments:
// - column - the column to get the attribute for
// Return Value:
// - the attribute
// Note: will throw exception if column is out of bounds
DbcsAttribute& CharRow::DbcsAttrAt(const size_t column)
{
return _data.at(column).DbcsAttr();
}
// Routine Description:
// - resets text data at column
// Arguments:
// - column - column index to clear text data from
// Return Value:
// - <none>
// Note: will throw exception if column is out of bounds
void CharRow::ClearGlyph(const size_t column)
{
_data.at(column).EraseChars();
}
// Routine Description:
// - returns text data at column as a const reference.
// Arguments:
// - column - column to get text data for
// Return Value:
// - text data at column
// - Note: will throw exception if column is out of bounds
const CharRow::reference CharRow::GlyphAt(const size_t column) const
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
return { const_cast<CharRow&>(*this), column };
}
// Routine Description:
// - returns text data at column as a reference.
// Arguments:
// - column - column to get text data for
// Return Value:
// - text data at column
// - Note: will throw exception if column is out of bounds
CharRow::reference CharRow::GlyphAt(const size_t column)
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
return { *this, column };
}
std::wstring CharRow::GetText() const
{
std::wstring wstr;
wstr.reserve(_data.size());
for (size_t i = 0; i < _data.size(); ++i)
{
const auto glyph = GlyphAt(i);
if (!DbcsAttrAt(i).IsTrailing())
{
for (const auto wch : glyph)
{
wstr.push_back(wch);
}
}
}
return wstr;
}
// Method Description:
// - get delimiter class for a position in the char row
// - used for double click selection and uia word navigation
// Arguments:
// - column: column to get text data for
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
// Return Value:
// - the delimiter class for the given char
const DelimiterClass CharRow::DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
const auto glyph = *GlyphAt(column).begin();
if (glyph <= UNICODE_SPACE)
{
return DelimiterClass::ControlChar;
}
else if (wordDelimiters.find(glyph) != std::wstring_view::npos)
{
return DelimiterClass::DelimiterChar;
}
else
{
return DelimiterClass::RegularChar;
}
}
UnicodeStorage& CharRow::GetUnicodeStorage() noexcept
{
return _pParent->GetUnicodeStorage();
}
const UnicodeStorage& CharRow::GetUnicodeStorage() const noexcept
{
return _pParent->GetUnicodeStorage();
}
// Routine Description:
// - calculates the key used by the given column of the char row to store glyph data in UnicodeStorage
// Arguments:
// - column - the column to generate the key for
// Return Value:
// - the COORD key for data access from UnicodeStorage for the column
COORD CharRow::GetStorageKey(const size_t column) const noexcept
{
return { gsl::narrow<SHORT>(column), _pParent->GetId() };
}
// Routine Description:
// - Updates the pointer to the parent row (which might change if we shuffle the rows around)
// Arguments:
// - pParent - Pointer to the parent row
void CharRow::UpdateParent(ROW* const pParent)
{
_pParent = FAIL_FAST_IF_NULL(pParent);
}

View File

@@ -1,115 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CharRow.hpp
Abstract:
- contains data structure for UCS2 encoded character data of a row
Author(s):
- Michael Niksa (miniksa) 10-Apr-2014
- Paul Campbell (paulcam) 10-Apr-2014
Revision History:
- From components of output.h/.c
by Therese Stowell (ThereseS) 1990-1991
- Pulled into its own file from textBuffer.hpp/cpp (AustDi, 2017)
--*/
#pragma once
#include "DbcsAttribute.hpp"
#include "CharRowCellReference.hpp"
#include "CharRowCell.hpp"
#include "UnicodeStorage.hpp"
class ROW;
enum class DelimiterClass
{
ControlChar,
DelimiterChar,
RegularChar
};
// the characters of one row of screen buffer
// we keep the following values so that we don't write
// more pixels to the screen than we have to:
// left is initialized to screenbuffer width. right is
// initialized to zero.
//
// [ foo.bar 12-12-61 ]
// ^ ^ ^ ^
// | | | |
// Chars Left Right end of Chars buffer
class CharRow final
{
public:
using glyph_type = typename wchar_t;
using value_type = typename CharRowCell;
using iterator = typename boost::container::small_vector_base<value_type>::iterator;
using const_iterator = typename boost::container::small_vector_base<value_type>::const_iterator;
using const_reverse_iterator = typename boost::container::small_vector_base<value_type>::const_reverse_iterator;
using reference = typename CharRowCellReference;
CharRow(size_t rowWidth, ROW* const pParent) noexcept;
size_t size() const noexcept;
[[nodiscard]] HRESULT Resize(const size_t newSize) noexcept;
size_t MeasureLeft() const noexcept;
size_t MeasureRight() const;
bool ContainsText() const noexcept;
const DbcsAttribute& DbcsAttrAt(const size_t column) const;
DbcsAttribute& DbcsAttrAt(const size_t column);
void ClearGlyph(const size_t column);
const DelimiterClass DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const;
// working with glyphs
const reference GlyphAt(const size_t column) const;
reference GlyphAt(const size_t column);
// iterators
iterator begin() noexcept;
const_iterator cbegin() const noexcept;
const_iterator begin() const noexcept { return cbegin(); }
iterator end() noexcept;
const_iterator cend() const noexcept;
const_iterator end() const noexcept { return cend(); }
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
COORD GetStorageKey(const size_t column) const noexcept;
void UpdateParent(ROW* const pParent);
friend CharRowCellReference;
friend class ROW;
private:
void Reset() noexcept;
void ClearCell(const size_t column);
std::wstring GetText() const;
protected:
// storage for glyph data and dbcs attributes
boost::container::small_vector<value_type, 120> _data;
// ROW that this CharRow belongs to
ROW* _pParent;
};
template<typename InputIt1, typename InputIt2>
void OverwriteColumns(InputIt1 startChars, InputIt1 endChars, InputIt2 startAttrs, CharRow::iterator outIt)
{
std::transform(startChars,
endChars,
startAttrs,
outIt,
[](const wchar_t wch, const DbcsAttribute attr) {
return CharRow::value_type{ wch, attr };
});
}

View File

@@ -1,73 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "CharRowCell.hpp"
#include "unicode.hpp"
// default glyph value, used for resetting the character data portion of a cell
static constexpr wchar_t DefaultValue = UNICODE_SPACE;
// Routine Description:
// - "erases" the glyph. really sets it back to the default "empty" value
void CharRowCell::EraseChars() noexcept
{
if (_attr.IsGlyphStored())
{
_attr.SetGlyphStored(false);
}
_wch = DefaultValue;
}
// Routine Description:
// - resets this object back to the defaults it would have from the default constructor
void CharRowCell::Reset() noexcept
{
_attr.Reset();
_wch = DefaultValue;
}
// Routine Description:
// - checks if cell contains a space glyph
// Return Value:
// - true if cell contains a space glyph, false otherwise
bool CharRowCell::IsSpace() const noexcept
{
return !_attr.IsGlyphStored() && _wch == UNICODE_SPACE;
}
// Routine Description:
// - Access the DbcsAttribute for the cell
// Return Value:
// - ref to the cells' DbcsAttribute
DbcsAttribute& CharRowCell::DbcsAttr() noexcept
{
return _attr;
}
// Routine Description:
// - Access the DbcsAttribute for the cell
// Return Value:
// - ref to the cells' DbcsAttribute
const DbcsAttribute& CharRowCell::DbcsAttr() const noexcept
{
return _attr;
}
// Routine Description:
// - Access the cell's wchar field. this does not access any char data through UnicodeStorage.
// Return Value:
// - the cell's wchar field
wchar_t& CharRowCell::Char() noexcept
{
return _wch;
}
// Routine Description:
// - Access the cell's wchar field. this does not access any char data through UnicodeStorage.
// Return Value:
// - the cell's wchar field
const wchar_t& CharRowCell::Char() const noexcept
{
return _wch;
}

View File

@@ -1,65 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CharRowCell.hpp
Abstract:
- data structure for one cell of a char row. contains the char data for one
coordinate position in the output buffer (leading/trailing information and
the char itself.
Author(s):
- Austin Diviness (AustDi) 02-May-2018
--*/
#pragma once
#include "DbcsAttribute.hpp"
#include "unicode.hpp"
#if (defined(_M_IX86) || defined(_M_AMD64))
// currently CharRowCell's fields use 3 bytes of memory, leaving the 4th byte in unused. this leads
// to a rather large amount of useless memory allocated. so instead, pack CharRowCell by bytes instead of words.
#pragma pack(push, 1)
#endif
class CharRowCell final
{
public:
CharRowCell() noexcept = default;
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept
:
_wch(wch),
_attr(attr)
{
}
void EraseChars() noexcept;
void Reset() noexcept;
bool IsSpace() const noexcept;
DbcsAttribute& DbcsAttr() noexcept;
const DbcsAttribute& DbcsAttr() const noexcept;
wchar_t& Char() noexcept;
const wchar_t& Char() const noexcept;
friend constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept;
private:
wchar_t _wch{ UNICODE_SPACE };
DbcsAttribute _attr{};
};
#if (defined(_M_IX86) || defined(_M_AMD64))
#pragma pack(pop)
#endif
constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept
{
return (a._wch == b._wch &&
a._attr == b._attr);
}

View File

@@ -1,136 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "UnicodeStorage.hpp"
#include "CharRow.hpp"
// Routine Description:
// - assignment operator. will store extended glyph data in a separate storage location
// Arguments:
// - chars - the glyph data to store
void CharRowCellReference::operator=(const std::wstring_view chars)
{
THROW_HR_IF(E_INVALIDARG, chars.empty());
if (chars.size() == 1)
{
_cellData().Char() = chars.front();
_cellData().DbcsAttr().SetGlyphStored(false);
}
else
{
auto& storage = _parent.GetUnicodeStorage();
const auto key = _parent.GetStorageKey(_index);
storage.StoreGlyph(key, { chars.cbegin(), chars.cend() });
_cellData().DbcsAttr().SetGlyphStored(true);
}
}
// Routine Description:
// - implicit conversion to vector<wchar_t> operator.
// Return Value:
// - std::vector<wchar_t> of the glyph data in the referenced cell
CharRowCellReference::operator std::wstring_view() const
{
return _glyphData();
}
// Routine Description:
// - The CharRowCell this object "references"
// Return Value:
// - ref to the CharRowCell
CharRowCell& CharRowCellReference::_cellData()
{
return _parent._data.at(_index);
}
// Routine Description:
// - The CharRowCell this object "references"
// Return Value:
// - ref to the CharRowCell
const CharRowCell& CharRowCellReference::_cellData() const
{
return _parent._data.at(_index);
}
// Routine Description:
// - the glyph data of the referenced cell
// Return Value:
// - the glyph data
std::wstring_view CharRowCellReference::_glyphData() const
{
if (_cellData().DbcsAttr().IsGlyphStored())
{
const auto& text = _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index));
return { text.data(), text.size() };
}
else
{
return { &_cellData().Char(), 1 };
}
}
// Routine Description:
// - gets read-only iterator to the beginning of the glyph data
// Return Value:
// - iterator of the glyph data
CharRowCellReference::const_iterator CharRowCellReference::begin() const
{
if (_cellData().DbcsAttr().IsGlyphStored())
{
return _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index)).data();
}
else
{
return &_cellData().Char();
}
}
// Routine Description:
// - get read-only iterator to the end of the glyph data
// Return Value:
// - end iterator of the glyph data
#pragma warning(push)
#pragma warning(disable : 26481)
// TODO GH 2672: eliminate using pointers raw as begin/end markers in this class
CharRowCellReference::const_iterator CharRowCellReference::end() const
{
if (_cellData().DbcsAttr().IsGlyphStored())
{
const auto& chars = _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index));
return chars.data() + chars.size();
}
else
{
return &_cellData().Char() + 1;
}
}
#pragma warning(pop)
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph)
{
const DbcsAttribute& dbcsAttr = ref._cellData().DbcsAttr();
if (glyph.size() == 1 && dbcsAttr.IsGlyphStored())
{
return false;
}
else if (glyph.size() > 1 && !dbcsAttr.IsGlyphStored())
{
return false;
}
else if (glyph.size() == 1 && !dbcsAttr.IsGlyphStored())
{
return ref._cellData().Char() == glyph.front();
}
else
{
const auto& chars = ref._parent.GetUnicodeStorage().GetText(ref._parent.GetStorageKey(ref._index));
return chars == glyph;
}
}
bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref)
{
return ref == glyph;
}

View File

@@ -1,63 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CharRowCellReference.hpp
Abstract:
- reference class for the glyph data of a char row cell
Author(s):
- Austin Diviness (AustDi) 02-May-2018
--*/
#pragma once
#include "DbcsAttribute.hpp"
#include "CharRowCell.hpp"
#include <utility>
class CharRow;
class CharRowCellReference final
{
public:
using const_iterator = const wchar_t*;
CharRowCellReference(CharRow& parent, const size_t index) noexcept :
_parent{ parent },
_index{ index }
{
}
~CharRowCellReference() = default;
CharRowCellReference(const CharRowCellReference&) noexcept = default;
CharRowCellReference(CharRowCellReference&&) noexcept = default;
void operator=(const CharRowCellReference&) = delete;
void operator=(CharRowCellReference&&) = delete;
void operator=(const std::wstring_view chars);
operator std::wstring_view() const;
const_iterator begin() const;
const_iterator end() const;
friend bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph);
friend bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref);
private:
// what char row the object belongs to
CharRow& _parent;
// the index of the cell in the parent char row
const size_t _index;
CharRowCell& _cellData();
const CharRowCell& _cellData() const;
std::wstring_view _glyphData() const;
};
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph);
bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref);

View File

@@ -27,14 +27,12 @@ public:
};
DbcsAttribute() noexcept :
_attribute{ Attribute::Single },
_glyphStored{ false }
_attribute{ Attribute::Single }
{
}
DbcsAttribute(const Attribute attribute) noexcept :
_attribute{ attribute },
_glyphStored{ false }
_attribute{ attribute }
{
}
@@ -58,16 +56,6 @@ public:
return IsLeading() || IsTrailing();
}
constexpr bool IsGlyphStored() const noexcept
{
return _glyphStored;
}
void SetGlyphStored(const bool stored) noexcept
{
_glyphStored = stored;
}
void SetSingle() noexcept
{
_attribute = Attribute::Single;
@@ -86,7 +74,6 @@ public:
void Reset() noexcept
{
SetSingle();
SetGlyphStored(false);
}
WORD GeneratePublicApiAttributeFormat() const noexcept
@@ -127,7 +114,6 @@ public:
private:
Attribute _attribute : 2;
bool _glyphStored : 1;
#ifdef UNIT_TESTING
friend class TextBufferTests;

View File

@@ -1,546 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "OutputCellIterator.hpp"
#include "../../types/inc/convert.hpp"
#include "../../types/inc/Utf16Parser.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../inc/conattrs.hpp"
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR };
// Routine Description:
// - This is a fill-mode iterator for one particular wchar. It will repeat forever if fillLimit is 0.
// Arguments:
// - wch - The character to use for filling
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(wch)),
_run(),
_attr(InvalidTextAttribute),
_pos(0),
_distance(0),
_fillLimit(fillLimit)
{
}
// Routine Description:
// - This is a fill-mode iterator for one particular color. It will repeat forever if fillLimit is 0.
// Arguments:
// - attr - The color attribute to use for filling
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(attr)),
_run(),
_attr(InvalidTextAttribute),
_pos(0),
_distance(0),
_fillLimit(fillLimit)
{
}
// Routine Description:
// - This is a fill-mode iterator for one particular character and color. It will repeat forever if fillLimit is 0.
// Arguments:
// - wch - The character to use for filling
// - attr - The color attribute to use for filling
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(wch, attr)),
_run(),
_attr(InvalidTextAttribute),
_pos(0),
_distance(0),
_fillLimit(fillLimit)
{
}
// Routine Description:
// - This is a fill-mode iterator for one particular CHAR_INFO. It will repeat forever if fillLimit is 0.
// Arguments:
// - charInfo - The legacy character and color data to use for filling (uses Unicode portion of text data)
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) noexcept :
_mode(Mode::Fill),
_currentView(s_GenerateView(charInfo)),
_run(),
_attr(InvalidTextAttribute),
_pos(0),
_distance(0),
_fillLimit(fillLimit)
{
}
// Routine Description:
// - This is an iterator over a range of text only. No color data will be modified as the text is inserted.
// Arguments:
// - utf16Text - UTF-16 text range
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text) :
_mode(Mode::LooseTextOnly),
_currentView(s_GenerateView(utf16Text)),
_run(utf16Text),
_attr(InvalidTextAttribute),
_pos(0),
_distance(0),
_fillLimit(0)
{
}
// Routine Description:
// - This is an iterator over a range text that will apply the same color to every position.
// Arguments:
// - utf16Text - UTF-16 text range
// - attribute - Color to apply over the entire range
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute attribute) :
_mode(Mode::Loose),
_currentView(s_GenerateView(utf16Text, attribute)),
_run(utf16Text),
_attr(attribute),
_distance(0),
_pos(0),
_fillLimit(0)
{
}
// Routine Description:
// - This is an iterator over legacy colors only. The text is not modified.
// Arguments:
// - legacyAttrs - One legacy color item per cell
OutputCellIterator::OutputCellIterator(const gsl::span<const WORD> legacyAttrs) noexcept :
_mode(Mode::LegacyAttr),
_currentView(s_GenerateViewLegacyAttr(til::at(legacyAttrs, 0))),
_run(legacyAttrs),
_attr(InvalidTextAttribute),
_distance(0),
_pos(0),
_fillLimit(0)
{
}
// Routine Description:
// - This is an iterator over legacy cell data. We will use the unicode text and the legacy color attribute.
// Arguments:
// - charInfos - Multiple cell with unicode text and legacy color data.
OutputCellIterator::OutputCellIterator(const gsl::span<const CHAR_INFO> charInfos) noexcept :
_mode(Mode::CharInfo),
_currentView(s_GenerateView(til::at(charInfos, 0))),
_run(charInfos),
_attr(InvalidTextAttribute),
_distance(0),
_pos(0),
_fillLimit(0)
{
}
// Routine Description:
// - This is an iterator over existing OutputCells with full text and color data.
// Arguments:
// - cells - Multiple cells in a run
OutputCellIterator::OutputCellIterator(const gsl::span<const OutputCell> cells) :
_mode(Mode::Cell),
_currentView(s_GenerateView(til::at(cells, 0))),
_run(cells),
_attr(InvalidTextAttribute),
_distance(0),
_pos(0),
_fillLimit(0)
{
}
// Routine Description:
// - Specifies whether this iterator is valid for dereferencing (still valid underlying data)
// Return Value:
// - True if the views on dereference are valid. False if it shouldn't be dereferenced.
OutputCellIterator::operator bool() const noexcept
{
try
{
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<gsl::span<const OutputCell>>(_run).size();
}
case Mode::CharInfo:
{
return _pos < std::get<gsl::span<const CHAR_INFO>>(_run).size();
}
case Mode::LegacyAttr:
{
return _pos < std::get<gsl::span<const WORD>>(_run).size();
}
default:
FAIL_FAST_HR(E_NOTIMPL);
}
}
CATCH_FAIL_FAST();
}
// Routine Description:
// - Advances the iterator one position over the underlying data source.
// Return Value:
// - Reference to self after advancement.
OutputCellIterator& OutputCellIterator::operator++()
{
// Keep track of total distance moved (cells filled)
_distance++;
switch (_mode)
{
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
// in case we had a surrogate pair (or wider complex sequence) in the previous view.
_pos += _currentView.Chars().size();
if (operator bool())
{
_currentView = s_GenerateView(std::get<std::wstring_view>(_run).substr(_pos), _attr);
}
}
break;
}
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
// in case we had a surrogate pair (or wider complex sequence) in the previous view.
_pos += _currentView.Chars().size();
if (operator bool())
{
_currentView = s_GenerateView(std::get<std::wstring_view>(_run).substr(_pos));
}
}
break;
}
case Mode::Fill:
{
if (!_TryMoveTrailing())
{
if (_currentView.DbcsAttr().IsTrailing())
{
auto dbcsAttr = _currentView.DbcsAttr();
dbcsAttr.SetLeading();
_currentView = OutputCellView(_currentView.Chars(),
dbcsAttr,
_currentView.TextAttr(),
_currentView.TextAttrBehavior());
}
if (_fillLimit > 0)
{
// We walk forward by one because we fill with the same cell over and over no matter what
_pos++;
}
}
break;
}
case Mode::Cell:
{
// Walk forward by one because cells are assumed to be in the form they needed to be
_pos++;
if (operator bool())
{
_currentView = s_GenerateView(til::at(std::get<gsl::span<const OutputCell>>(_run), _pos));
}
break;
}
case Mode::CharInfo:
{
// Walk forward by one because charinfos are just the legacy version of cells and prealigned to columns
_pos++;
if (operator bool())
{
_currentView = s_GenerateView(til::at(std::get<gsl::span<const CHAR_INFO>>(_run), _pos));
}
break;
}
case Mode::LegacyAttr:
{
// Walk forward by one because color attributes apply cell by cell (no complex text information)
_pos++;
if (operator bool())
{
_currentView = s_GenerateViewLegacyAttr(til::at(std::get<gsl::span<const WORD>>(_run), _pos));
}
break;
}
default:
FAIL_FAST_HR(E_NOTIMPL);
}
return (*this);
}
// Routine Description:
// - Advances the iterator one position over the underlying data source.
// Return Value:
// - Reference to self after advancement.
OutputCellIterator OutputCellIterator::operator++(int)
{
auto temp(*this);
operator++();
return temp;
}
// Routine Description:
// - Reference the view to fully-formed output cell data representing the underlying data source.
// Return Value:
// - Reference to the view
const OutputCellView& OutputCellIterator::operator*() const noexcept
{
return _currentView;
}
// Routine Description:
// - Get pointer to the view to fully-formed output cell data representing the underlying data source.
// Return Value:
// - Pointer to the view
const OutputCellView* OutputCellIterator::operator->() const noexcept
{
return &_currentView;
}
// Routine Description:
// - Checks the current view. If it is a leading half, it updates the current
// view to the trailing half of the same glyph.
// - This helps us to draw glyphs that are two columns wide by "doubling"
// the view that is returned so it will consume two cells.
// Return Value:
// - True if we just turned a lead half into a trailing half (and caller doesn't
// need to further update the view).
// - False if this wasn't applicable and the caller should update the view.
bool OutputCellIterator::_TryMoveTrailing() noexcept
{
if (_currentView.DbcsAttr().IsLeading())
{
auto dbcsAttr = _currentView.DbcsAttr();
dbcsAttr.SetTrailing();
_currentView = OutputCellView(_currentView.Chars(),
dbcsAttr,
_currentView.TextAttr(),
_currentView.TextAttrBehavior());
return true;
}
else
{
return false;
}
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and specify that the attributes shouldn't be changed.
// Arguments:
// - view - View representing characters corresponding to a single glyph
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view)
{
return s_GenerateView(view, InvalidTextAttribute, TextAttributeBehavior::Current);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - view - View representing characters corresponding to a single glyph
// - attr - Color attributes to apply to the text
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
const TextAttribute attr)
{
return s_GenerateView(view, attr, TextAttributeBehavior::Stored);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - view - View representing characters corresponding to a single glyph
// - attr - Color attributes to apply to the text
// - behavior - Behavior of the given text attribute (used when writing)
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
const TextAttribute attr,
const TextAttributeBehavior behavior)
{
const auto glyph = Utf16Parser::ParseNext(view);
DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(glyph))
{
dbcsAttr.SetLeading();
}
return OutputCellView(glyph, dbcsAttr, attr, behavior);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - wch - View representing a single UTF-16 character (that can be represented without surrogates)
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) noexcept
{
const auto glyph = std::wstring_view(&wch, 1);
DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(wch))
{
dbcsAttr.SetLeading();
}
return OutputCellView(glyph, dbcsAttr, InvalidTextAttribute, TextAttributeBehavior::Current);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - attr - View representing a single color
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) noexcept
{
return OutputCellView({}, {}, attr, TextAttributeBehavior::StoredOnly);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - wch - View representing a single UTF-16 character (that can be represented without surrogates)
// - attr - View representing a single color
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept
{
const auto glyph = std::wstring_view(&wch, 1);
DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(wch))
{
dbcsAttr.SetLeading();
}
return OutputCellView(glyph, dbcsAttr, attr, TextAttributeBehavior::Stored);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - legacyAttr - View representing a single legacy color
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept
{
WORD cleanAttr = legacyAttr;
WI_ClearAllFlags(cleanAttr, COMMON_LVB_SBCSDBCS); // don't use legacy lead/trailing byte flags for colors
const TextAttribute attr(cleanAttr);
return s_GenerateView(attr);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// - This will infer the width of the glyph and apply the appropriate attributes to the view.
// Arguments:
// - charInfo - character and attribute pair representing a single cell
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo) noexcept
{
const auto glyph = std::wstring_view(&charInfo.Char.UnicodeChar, 1);
DbcsAttribute dbcsAttr;
if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_LEADING_BYTE))
{
dbcsAttr.SetLeading();
}
else if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_TRAILING_BYTE))
{
dbcsAttr.SetTrailing();
}
const TextAttribute textAttr(charInfo.Attributes);
const auto behavior = TextAttributeBehavior::Stored;
return OutputCellView(glyph, dbcsAttr, textAttr, behavior);
}
// Routine Description:
// - Static function to create a view.
// - It's pulled out statically so it can be used during construction with just the given
// variables (so OutputCellView doesn't need an empty default constructor)
// Arguments:
// - cell - A reference to the cell for which we will make the read-only view
// Return Value:
// - Object representing the view into this cell
OutputCellView OutputCellIterator::s_GenerateView(const OutputCell& cell)
{
return OutputCellView(cell.Chars(), cell.DbcsAttr(), cell.TextAttr(), cell.TextAttrBehavior());
}
// Routine Description:
// - Gets the distance between two iterators relative to the input data given in.
// Return Value:
// - The number of items of the input run consumed between these two iterators.
ptrdiff_t OutputCellIterator::GetInputDistance(OutputCellIterator other) const noexcept
{
return _pos - other._pos;
}
// Routine Description:
// - Gets the distance between two iterators relative to the number of cells inserted.
// Return Value:
// - The number of cells in the backing buffer filled between these two iterators.
ptrdiff_t OutputCellIterator::GetCellDistance(OutputCellIterator other) const noexcept
{
return _distance - other._distance;
}

View File

@@ -1,125 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- OutputCellIterator.hpp
Abstract:
- Read-only view into an entire batch of data to be written into the output buffer.
- This is done for performance reasons (avoid heap allocs and copies).
Author:
- Michael Niksa (MiNiksa) 06-Oct-2018
Revision History:
- Based on work from OutputCell.hpp/cpp by Austin Diviness (AustDi)
--*/
#pragma once
#include "TextAttribute.hpp"
#include "OutputCell.hpp"
#include "OutputCellView.hpp"
class OutputCellIterator final
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = OutputCellView;
using difference_type = ptrdiff_t;
using pointer = OutputCellView*;
using reference = OutputCellView&;
OutputCellIterator(const wchar_t& wch, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept;
OutputCellIterator(const std::wstring_view utf16Text);
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute attribute);
OutputCellIterator(const gsl::span<const WORD> legacyAttributes) noexcept;
OutputCellIterator(const gsl::span<const CHAR_INFO> charInfos) noexcept;
OutputCellIterator(const gsl::span<const OutputCell> cells);
~OutputCellIterator() = default;
OutputCellIterator& operator=(const OutputCellIterator& it) = default;
operator bool() const noexcept;
ptrdiff_t GetCellDistance(OutputCellIterator other) const noexcept;
ptrdiff_t GetInputDistance(OutputCellIterator other) const noexcept;
friend ptrdiff_t operator-(OutputCellIterator one, OutputCellIterator two) = delete;
OutputCellIterator& operator++();
OutputCellIterator operator++(int);
const OutputCellView& operator*() const noexcept;
const OutputCellView* operator->() const noexcept;
private:
enum class Mode
{
// Loose mode is where we're given text and attributes in a raw sort of form
// like while data is being inserted from an API call.
Loose,
// Loose mode with only text is where we're given just text and we want
// to use the attribute already in the buffer when writing
LooseTextOnly,
// Fill mode is where we were given one thing and we just need to keep giving
// that back over and over for eternity.
Fill,
// Given a run of legacy attributes, convert each of them and insert only attribute data.
LegacyAttr,
// CharInfo mode is where we've been given a pair of text and attribute for each
// cell in the legacy format from an API call.
CharInfo,
// Cell mode is where we have an already fully structured cell data usually
// from accessing/copying data already put into the OutputBuffer.
Cell,
};
Mode _mode;
gsl::span<const WORD> _legacyAttrs;
std::variant<
std::wstring_view,
gsl::span<const WORD>,
gsl::span<const CHAR_INFO>,
gsl::span<const OutputCell>,
std::monostate>
_run;
TextAttribute _attr;
bool _TryMoveTrailing() noexcept;
static OutputCellView s_GenerateView(const std::wstring_view view);
static OutputCellView s_GenerateView(const std::wstring_view view,
const TextAttribute attr);
static OutputCellView s_GenerateView(const std::wstring_view view,
const TextAttribute attr,
const TextAttributeBehavior behavior);
static OutputCellView s_GenerateView(const wchar_t& wch) noexcept;
static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept;
static OutputCellView s_GenerateView(const TextAttribute& attr) noexcept;
static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept;
static OutputCellView s_GenerateView(const CHAR_INFO& charInfo) noexcept;
static OutputCellView s_GenerateView(const OutputCell& cell);
OutputCellView _currentView;
size_t _pos;
size_t _distance;
size_t _fillLimit;
};

View File

@@ -42,19 +42,6 @@ gsl::span<OutputCell> OutputCellRect::GetRow(const size_t row)
return gsl::span<OutputCell>(_FindRowOffset(row), _cols);
}
// Routine Description:
// - Gets a read-only iterator view over a single row of the rectangle.
// Arguments:
// - row - The Y position or row index in the buffer.
// Return Value:
// - Read-only iterator of OutputCells
OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const
{
const gsl::span<const OutputCell> view(_FindRowOffset(row), _cols);
return OutputCellIterator(view);
}
// Routine Description:
// - Internal helper to find the pointer to the specific row offset in the giant
// contiguous block of memory allocated for this rectangle.

View File

@@ -21,10 +21,8 @@ Revision History:
#pragma once
#include "DbcsAttribute.hpp"
#include "TextAttribute.hpp"
#include "OutputCell.hpp"
#include "OutputCellIterator.hpp"
class OutputCellRect final
{
@@ -33,7 +31,6 @@ public:
OutputCellRect(const size_t rows, const size_t cols);
gsl::span<OutputCell> GetRow(const size_t row);
OutputCellIterator GetRowIter(const size_t row) const;
size_t Height() const noexcept;
size_t Width() const noexcept;

View File

@@ -3,7 +3,6 @@
#include "precomp.h"
#include "Row.hpp"
#include "CharRow.hpp"
#include "textBuffer.hpp"
#include "../types/inc/convert.hpp"
@@ -16,15 +15,16 @@
// - pParent - the text buffer that this row belongs to
// Return Value:
// - constructed object
ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) :
_id{ rowId },
_rowWidth{ rowWidth },
_charRow{ rowWidth, this },
_attrRow{ rowWidth, fillAttribute },
ROW::ROW(const SHORT /*rowId*/, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const /*pParent*/) :
_data{
std::wstring(rowWidth, UNICODE_SPACE),
{ { gsl::narrow_cast<uint8_t>(1), gsl::narrow_cast<uint16_t>(rowWidth) } },
{ rowWidth, fillAttribute },
rowWidth
},
_lineRendition{ LineRendition::SingleWidth },
_wrapForced{ false },
_doubleBytePadded{ false },
_pParent{ pParent }
_doubleBytePadded{ false }
{
}
@@ -39,10 +39,11 @@ bool ROW::Reset(const TextAttribute Attr)
_lineRendition = LineRendition::SingleWidth;
_wrapForced = false;
_doubleBytePadded = false;
_charRow.Reset();
_data._cwid.replace(0, _data._width, { 1, _data._width }); // replace entire RLE with one run
_data._data.replace(0, _data._width, _data._width, UNICODE_SPACE);
try
{
_attrRow.Reset(Attr);
_data._attrRow.Reset(Attr);
}
catch (...)
{
@@ -60,14 +61,20 @@ bool ROW::Reset(const TextAttribute Attr)
// - S_OK if successful, otherwise relevant error
[[nodiscard]] HRESULT ROW::Resize(const unsigned short width)
{
RETURN_IF_FAILED(_charRow.Resize(width));
_data._data.resize(width, L' ');
auto oldEnd{ _data._cwid.size() };
_data._cwid.resize_trailing_extent(width);
if (width > oldEnd)
{
_data._cwid.replace(oldEnd, width, { 1, gsl::narrow_cast<uint16_t>(width - oldEnd) });
}
try
{
_attrRow.Resize(width);
_data._attrRow.Resize(width);
}
CATCH_RETURN();
_rowWidth = width;
_data._width = width;
return S_OK;
}
@@ -80,20 +87,12 @@ bool ROW::Reset(const TextAttribute Attr)
// - <none>
void ROW::ClearColumn(const size_t column)
{
THROW_HR_IF(E_INVALIDARG, column >= _charRow.size());
_charRow.ClearCell(column);
}
UnicodeStorage& ROW::GetUnicodeStorage() noexcept
{
return _pParent->GetUnicodeStorage();
}
const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
{
return _pParent->GetUnicodeStorage();
THROW_HR_IF(E_INVALIDARG, column >= _data._width);
WriteGlyphAtMeasured(column, 1, L" ");
}
#if 0
TODO (DH)
// Routine Description:
// - writes cell data to the row
// Arguments:
@@ -105,11 +104,11 @@ const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
// - iterator to first cell that was not written to this row.
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap, std::optional<size_t> limitRight)
{
THROW_HR_IF(E_INVALIDARG, index >= _charRow.size());
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= _charRow.size());
THROW_HR_IF(E_INVALIDARG, index >= _data._width);
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= _data._width);
// 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);
const auto finalColumnInRow = limitRight.value_or(_data._width - 1);
auto currentColor = it->TextAttr();
uint16_t colorUses = 0;
@@ -131,7 +130,7 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
{
// Otherwise, commit this color into the run and save off the new one.
// Now commit the new color runs into the attr row.
_attrRow.Replace(colorStarts, currentIndex, currentColor);
_data._attrRow.Replace(colorStarts, currentIndex, currentColor);
currentColor = it->TextAttr();
colorUses = 1;
colorStarts = currentIndex;
@@ -151,21 +150,31 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
// 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);
ClearColumn(currentIndex);
it.AddCellDistanceFault(1); // we couldn't fit a cell here but we skipped a column :|
}
// 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);
ClearColumn(currentIndex);
it.AddCellDistanceFault(1); // we couldn't fit a cell here but we skipped a column :|
SetDoubleBytePadded(true);
}
// Otherwise, copy the data given and increment the iterator.
else
{
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
_charRow.GlyphAt(currentIndex) = it->Chars();
++it;
if (!it->DbcsAttr().IsTrailing())
{
uint16_t d = it->DbcsAttr().IsSingle() ? 1 : 2;
WriteGlyphAtMeasured(currentIndex, d, it->Chars());
currentIndex += d - 1;
while (d > 0)
{ // TODO(DH) FFS
++it;
--d;
}
}
}
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
@@ -191,8 +200,9 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
// Now commit the final color into the attr row
if (colorUses)
{
_attrRow.Replace(colorStarts, currentIndex, currentColor);
_data._attrRow.Replace(colorStarts, currentIndex, currentColor);
}
return it;
}
#endif

View File

@@ -23,18 +23,157 @@ Revision History:
#include "AttrRow.hpp"
#include "LineRendition.hpp"
#include "OutputCell.hpp"
#include "OutputCellIterator.hpp"
#include "CharRow.hpp"
#include "UnicodeStorage.hpp"
#include "unicode.hpp"
#pragma warning(push)
#pragma warning(disable : 4267)
class TextBuffer;
using RowMeasurementBuffer = til::small_rle<uint8_t, uint16_t, 3>;
struct DamageRanges
{
size_t dataOffset;
size_t dataLength;
uint16_t firstColumn;
uint16_t lastColumnExclusive;
};
template<typename TRuns>
static DamageRanges DamageRangesForColumnInMeasurementBuffer(const TRuns& cwid, size_t col)
{
size_t currentCol{ 0 };
size_t currentWchar{ 0 };
auto it{ cwid.runs().cbegin() };
while (it != cwid.runs().cend())
{
// Each compressed pair tells us how many columns x N wchar_t
const auto colsCoveredByRun{ it->value * it->length };
if (currentCol + colsCoveredByRun > col)
{
// We want to break out of the loop to manually handle this run, because
// we've determined that it is the run that covers the column of interest.
break;
}
currentCol += colsCoveredByRun;
currentWchar += it->length;
it++;
}
if (it == cwid.runs().cend())
{
// this is an interesting case- somebody requested a column we cannot answer for.
// The string might actually have data, and the caller might be interested in where that data is.
// Ideally, we would return the index of the first char out-of-bounds, and the length of the remaining data as a single unit.
// We can't answer for how much space it takes up, though.
__debugbreak();
return { 0, 0, 0u, 0u };
//return { currentWchar, _data.size() - currentWchar, 0u, 0u };
}
// currentWchar is how many wchar_t we are into the string before processing this run
// currentCol is how many columns we've covered before processing this run
// We are *guaranteed* that the hit is in this run -- no need to check it->length
// col-currentCol is how many columns are left unaccounted for (how far into this run we need to go)
const auto colsLeftToCountInCurrentRun{ col - currentCol };
currentWchar += colsLeftToCountInCurrentRun / it->value; // one wch per column unit -- rounds down (correct behavior)
size_t lenInWchars{ 1 }; // the first hit takes up one wchar
// We use this to determine if we have exhausted every column this run can cough up.
// colsLeftToCountInCurrentRun is 0-indexed, but colsConsumedByRun is 1-indexed (index 0 consumes N columns, etc.)
// therefore, we reindex colsLeftToCountInCurrentRun and compare it to colsConsumedByRun
const auto colsConsumedFromRun{ colsLeftToCountInCurrentRun + it->value };
const auto colsCoveredByRun{ it->value * it->length };
// If we *have* consumed every column this run can provide, we must check the run after it:
// if it contributes "0" columns, it is actually a set of trailing code units.
if (colsConsumedFromRun >= colsCoveredByRun && it != cwid.runs().cend())
{
const auto nextRunIt{ it + 1 };
if (nextRunIt != cwid.runs().cend() && nextRunIt->value == 0)
{
// we were at the boundary of a column run, so if the next one is 0 it tells us that each
// wchar after it is a trailer
lenInWchars += nextRunIt->length;
}
}
return {
currentWchar, // wchar start
lenInWchars, // wchar size
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value)), // Column damage to the left (where we overlapped the right of a wide glyph)
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value) + it->value), // Column damage to the right (where we overlapped the left of a wide glyph)
};
}
class ROW;
struct RowImage
{
std::wstring _data;
RowMeasurementBuffer _cwid;
ATTR_ROW _attrRow;
uint16_t _width;
friend class ROW;
friend class TextBuffer;
RowImage() :
_data{}, _cwid{}, _attrRow{ {} }, _width{ 0 } {}
RowImage(const std::wstring& data, const RowMeasurementBuffer& cwid, ATTR_ROW attrRow, uint16_t width):
_data{data}, _cwid{cwid}, _attrRow{std::move(attrRow)}, _width{width} {}
// exclusive
std::tuple<RowImage, RowImage> split(uint16_t col) const
{
if (col >= _width)
{
return { *this, RowImage{} };
}
else if (col == 0)
{
return { RowImage{}, *this };
}
auto yes_more_fucking_damage_ranges = DamageRangesForColumnInMeasurementBuffer(_cwid, col);
// here's a dumb decision: when you split along a wide char, you get spaces over its damage on the left side.
// X X XX X X X
// ^ split
// X X S <- left
// XX X X X <- right
auto chopped_off = col == yes_more_fucking_damage_ranges.lastColumnExclusive ? 0 : /*didn't get whole thing*/ yes_more_fucking_damage_ranges.lastColumnExclusive - col;
auto chopped_on = col == yes_more_fucking_damage_ranges.lastColumnExclusive ? yes_more_fucking_damage_ranges.dataLength : 0;
auto lwid = _cwid.slice(0, yes_more_fucking_damage_ranges.dataOffset + chopped_on);
for (auto z{ chopped_off }; z > 0; --z)
{
lwid.append(1);
}
RowImage left{
_data.substr(0, yes_more_fucking_damage_ranges.dataOffset + chopped_on) + std::wstring(chopped_off, UNICODE_SPACE),
lwid,
_attrRow._data.slice(0, yes_more_fucking_damage_ranges.lastColumnExclusive),
col
};
RowImage right{
_data.substr(yes_more_fucking_damage_ranges.dataOffset + chopped_on),
_cwid.slice(yes_more_fucking_damage_ranges.dataOffset + chopped_on, _data.length()),
// we use first col here to catch the overlap
_attrRow._data.slice(yes_more_fucking_damage_ranges.firstColumn, _width),
::base::ClampSub(_width, col),
};
return { left, right };
}
};
enum class DelimiterClass
{
ControlChar,
DelimiterChar,
RegularChar
};
class ROW final
{
public:
ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent);
size_t size() const noexcept { return _rowWidth; }
size_t size() const noexcept { return _data._width; }
void SetWrapForced(const bool wrap) noexcept { _wrapForced = wrap; }
bool WasWrapForced() const noexcept { return _wrapForced; }
@@ -42,52 +181,357 @@ public:
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept { _doubleBytePadded = doubleBytePadded; }
bool WasDoubleBytePadded() const noexcept { return _doubleBytePadded; }
const CharRow& GetCharRow() const noexcept { return _charRow; }
CharRow& GetCharRow() noexcept { return _charRow; }
const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
const ATTR_ROW& GetAttrRow() const noexcept { return _data._attrRow; }
ATTR_ROW& GetAttrRow() noexcept { return _data._attrRow; }
LineRendition GetLineRendition() const noexcept { return _lineRendition; }
void SetLineRendition(const LineRendition lineRendition) noexcept { _lineRendition = lineRendition; }
SHORT GetId() const noexcept { return _id; }
void SetId(const SHORT id) noexcept { _id = id; }
bool Reset(const TextAttribute Attr);
[[nodiscard]] HRESULT Resize(const unsigned short width);
void ClearColumn(const size_t column);
std::wstring GetText() const { return _charRow.GetText(); }
UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
OutputCellIterator WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap = std::nullopt, std::optional<size_t> limitRight = std::nullopt);
std::wstring GetText() const { return _data._data; }
#ifdef UNIT_TESTING
friend constexpr bool operator==(const ROW& a, const ROW& b) noexcept;
friend class RowTests;
#endif
struct RowData
{
std::wstring _data;
RowMeasurementBuffer _cwid;
ATTR_ROW _attrRow;
unsigned short _width;
DamageRanges _damageForColumn(size_t col) const
{
return DamageRangesForColumnInMeasurementBuffer(_cwid, col);
}
DamageRanges _damageForColumns(size_t col, size_t ncols) const
{
// When we want to replace a column, or set of columns, with a glyph, we need to:
// * Figure out the physical extent of the character in that cell (UTF-16 code units).
// * Figure out the columnar extent of the character in that cell (how many columns it covers).
// * In the simple case (1->1, 2->2), there will be no damage.
// * In the complex case (2->1, 1->2, 2->2 with middle overlap), there *WILL* be damage.
// * Replace the physical character data in that cell with the new character data.
// * Insert padding characters to the left and right to account for damage.
//
// ## DAMAGE
// Damage is measured in the number of columns to the left
// and right of the new glyph that are now NO LONGER VALID because
// they were double-width characters that are being cut in half,
// or single-width characters that are collateral damage from stomping
// them with a double-width character.
auto damage{ _damageForColumn(col) };
const auto lastDamage{ _damageForColumn(col + ncols - 1 /*inclusive*/) };
// *INVARIANT* the beginning of the next column range must have a different beginning byte
// This column began at a different data index, so we have to delete its data too.
// Since it's contiguous, just increment len.
damage.dataLength = lastDamage.dataOffset + lastDamage.dataLength - damage.dataOffset;
damage.lastColumnExclusive = lastDamage.lastColumnExclusive;
return damage;
}
void _strikeDamageAndAdjust(size_t col, size_t ncols, size_t incomingCodeUnitCount, DamageRanges& range)
{
(void)incomingCodeUnitCount;
const bool damaged{ range.firstColumn < col || col + ncols < range.lastColumnExclusive };
if (damaged)
{
const auto damagedColumns{ range.lastColumnExclusive - range.firstColumn };
_data.replace(range.dataOffset, range.dataLength, damagedColumns, UNICODE_SPACE);
_cwid.replace(range.dataOffset, range.dataOffset + range.dataLength, { uint8_t{ 1 }, gsl::narrow_cast<uint16_t>(damagedColumns) });
// We may have replaced surrogate pairs/etc with fewer/more code units.
range.dataLength = damagedColumns;
}
}
};
private:
CharRow _charRow;
ATTR_ROW _attrRow;
RowData _data;
LineRendition _lineRendition;
SHORT _id;
unsigned short _rowWidth;
// Occurs when the user runs out of text in a given row and we're forced to wrap the cursor to the next line
bool _wrapForced;
// Occurs when the user runs out of text to support a double byte character and we're forced to the next line
bool _doubleBytePadded;
TextBuffer* _pParent; // non ownership pointer
public:
std::wstring_view GlyphAt(size_t col) const
{
const auto lookup{ _data._damageForColumn(col) };
return { _data._data.data() + lookup.dataOffset, lookup.dataLength };
}
std::pair<size_t, size_t> WriteGlyphAtMeasured(size_t col, size_t ncols, std::wstring_view glyph)
{
const auto [begin, len, minDamageColumn, maxDamageColumnExclusive]{ _data._damageForColumns(col, ncols) };
if (minDamageColumn == col && maxDamageColumnExclusive == col + ncols)
{
// We are only damaging as many columns as we are introducing -- no spillover (!)
// We can replace the code units in the data directly, and we can replace the
// column counts with [col, 0, 0...] (with as many zeroes as we need to account
// for any code units past the first.)
_data._data.replace(begin, len, glyph);
typename decltype(_data._cwid)::rle_type newRuns[]{
{ gsl::narrow_cast<uint8_t>(ncols), 1 },
{ 0, gsl::narrow_cast<uint16_t>(glyph.size() - 1) },
};
_data._cwid.replace(gsl::narrow_cast<uint16_t>(begin), gsl::narrow_cast<uint16_t>(begin + len), gsl::make_span(&newRuns[0], glyph.size() == 1 ? 1 : 2));
}
else
{
// We are damaging multiple columns -- oops. We need to insert replacement characters
// to get us from the leftmost side of the damaged glyph up to the leftmost side of
// our newly-inserted region. We also need to insert replacement characters from the
// rightmost side of our glyph to the rightmost side of the glyph that was once in
// that column.
// Left side count : col - minDamageColumn
// Right side count: maxDamageColumn - (col + ncols)
const auto replacementCodeUnits{ (col - minDamageColumn) + glyph.size() + (maxDamageColumnExclusive - (col + ncols)) };
std::wstring replacement(replacementCodeUnits, UNICODE_SPACE);
replacement.replace(col - minDamageColumn, glyph.size(), glyph);
// New advances:
// Our glyph and all its trailers
// v-----v
// [1, ..., 1, X, 0, 0, 1, ..., 1]
// ^-------^ ^-------^
// Each replacement space char
// is one column wide. We have
// to insert [1]s for each
// damaged column.
boost::container::small_vector<typename decltype(_data._cwid)::rle_type, 4> newRuns;
if (col - minDamageColumn)
{
newRuns.emplace_back((uint8_t)1, gsl::narrow_cast<uint16_t>(col - minDamageColumn));
}
newRuns.emplace_back(gsl::narrow_cast<uint8_t>(ncols), (uint16_t)1);
if (glyph.size() > 1)
{
newRuns.emplace_back((uint8_t)0, gsl::narrow_cast<uint16_t>(glyph.size() - 1)); // trailers
}
if (maxDamageColumnExclusive - (col + ncols))
{
newRuns.emplace_back((uint8_t)1, gsl::narrow_cast<uint16_t>(maxDamageColumnExclusive - (col + ncols)));
}
_data._data.replace(begin, len, replacement);
_data._cwid.replace(gsl::narrow_cast<uint16_t>(begin), gsl::narrow_cast<uint16_t>(begin + len), gsl::make_span(newRuns));
}
// Distance from requested column to final
_maxc = std::max(_maxc, maxDamageColumnExclusive);
return { begin + glyph.size(), col + ncols };
}
DbcsAttribute DbcsAttrAt(size_t col) const
{
const auto [begin, len, first, lastE] = _data._damageForColumn(col);
if (lastE - first == 1)
{
// The glyph under this column is only onw column wide.
return DbcsAttribute{ DbcsAttribute::Attribute::Single };
}
else if (first != col)
{
// The glyph under this column is >1 col wide, and we're bisecting it
return DbcsAttribute{ DbcsAttribute::Attribute::Trailing };
}
else
{
// The glyph under this column is >1 col wide, and we're at the head
return DbcsAttribute{ DbcsAttribute::Attribute::Leading };
}
}
std::tuple<size_t, uint16_t, uint16_t> WriteStringAtMeasured(uint16_t col, uint16_t colCount, const std::wstring_view& string, const RowMeasurementBuffer& measurements)
{
size_t incomingLastColumn{ std::min<size_t>(_data._width - col, colCount) };
auto incomingLastColumnOffsets{ DamageRangesForColumnInMeasurementBuffer(measurements, incomingLastColumn - 1 /*inclusive*/) };
auto codeUnitsToConsume{ incomingLastColumnOffsets.dataOffset };
auto columnsToConsume{ incomingLastColumn };
const auto [begin, len, minDamageColumn, maxDamageColumnExclusive]{ _data._damageForColumns(col, incomingLastColumn) };
// If these don't match, we are cutting a multi-cell glyph.
if (incomingLastColumnOffsets.lastColumnExclusive == incomingLastColumn)
{
// Since they *do* match, we should consume this part of the string too.
codeUnitsToConsume += incomingLastColumnOffsets.dataLength;
}
else
{
// Only consume up to the final cell (the one we cut in half)
columnsToConsume = incomingLastColumnOffsets.firstColumn;
// **INVARIANT** we only get here if we had to cut off the incoming text, and that only
// happens because we had to clamp the read buffer against our width. This means that the
// incoming text definitely had a wide glyph that would not fit against the end of our
// buffer.
// THEREFORE: col+incomingLastColumn was our final column (exclusive)
// which means that maxDamageColumnExclusive can be upgraded to be our width.
// OPTIMIZATION: If we mark our last column as damaged, it will automatically get stomped
// with spaces.
// NO NO NO NO NO NO NO NO NO NO TODO(DH)
// We can't do this without changing the damaged buffer region to delete the character
// from _data at the same time. Use the non-optimial path.
ClearColumn(_data._width - 1);
SetDoubleBytePadded(true);
// NO - we already marked this column when we calculated damage above
//++columnsWrittenAfterInsertionPoint;
}
auto mss = measurements.slice(0, gsl::narrow_cast<uint16_t>(codeUnitsToConsume));
if (minDamageColumn == col && maxDamageColumnExclusive == col + incomingLastColumn)
{
// We are only damaging as many columns as we are introducing -- no spillover (!)
// We can replace the code units in the data directly, and we can replace the
// column counts with [col, 0, 0...] (with as many zeroes as we need to account
// for any code units past the first.)
_data._data.replace(begin, len, &*string.cbegin(), codeUnitsToConsume);
_data._cwid.replace(gsl::narrow_cast<uint16_t>(begin), gsl::narrow_cast<uint16_t>(begin + len), mss.runs());
}
else
{
// We are damaging multiple columns -- oops. We need to insert replacement characters
// to get us from the leftmost side of the damaged glyph up to the leftmost side of
// our newly-inserted region. We also need to insert replacement characters from the
// rightmost side of our glyph to the rightmost side of the glyph that was once in
// that column.
// Left side count : col - minDamageColumn
// Right side count: maxDamageColumn - (col + ncols)
const auto replacementCodeUnits{ (col - minDamageColumn) + codeUnitsToConsume + (maxDamageColumnExclusive - (col + incomingLastColumn)) };
std::wstring replacement(replacementCodeUnits, UNICODE_SPACE);
replacement.replace(col - minDamageColumn, codeUnitsToConsume, &*string.cbegin(), codeUnitsToConsume);
// New advances:
// Our glyph and all its trailers
// v-----v
// [1, ..., 1, X, 0, 0, 1, ..., 1]
// ^-------^ ^-------^
// Each replacement space char
// is one column wide. We have
// to insert [1]s for each
// damaged column.
mss.replace(0, 0, { uint8_t{ 1 }, gsl::narrow_cast<uint16_t>(col - minDamageColumn) });
mss.replace(mss.size(), mss.size(), { uint8_t{ 1 }, gsl::narrow_cast<uint16_t>(maxDamageColumnExclusive - (col + incomingLastColumn)) });
_data._data.replace(begin, len, replacement);
_data._cwid.replace(gsl::narrow_cast<uint16_t>(begin), gsl::narrow_cast<uint16_t>(begin + len), mss.runs()); //gsl::make_span(newRuns));
}
return {
codeUnitsToConsume,
gsl::narrow_cast<uint16_t>(maxDamageColumnExclusive - col),
gsl::narrow_cast<uint16_t>(columnsToConsume)
};
}
size_t Fill(size_t col, size_t count, wchar_t ch, uint8_t w)
{
const auto charsFitOrRemain = std::min((_data._width - col) / w, count);
const auto columnsRequired{ charsFitOrRemain * w };
auto damage = _data._damageForColumns(col, columnsRequired);
// If we are filling over the left/right halves of a character
// This is a bit wasteful since it can grow/shrink the buffers and we're about
// to do it again, but I was trying to be expedient.
_data._strikeDamageAndAdjust(col, columnsRequired, charsFitOrRemain, damage);
const auto [begin, len, min, max] = damage;
_data._data.replace(begin, len, charsFitOrRemain, ch);
_data._cwid.replace(begin, begin + len, { w, gsl::narrow_cast<uint16_t>(charsFitOrRemain) });
const auto doubleBytePadded{
w > 1 // We had a wide glyph...
&& max != _data._width // ...and didn't reach the edge
&& count > charsFitOrRemain // ...but we had spare characters, so we wanted to
};
if (doubleBytePadded)
{
const uint16_t remaining{ gsl::narrow_cast<uint16_t>(_data._width - max) };
// overflow: add spaces
_data._data.replace(begin + charsFitOrRemain, _data._data.size() - begin + charsFitOrRemain, remaining, UNICODE_SPACE);
_data._cwid.replace(begin + charsFitOrRemain, _data._cwid.size(), { uint8_t{ 1u }, gsl::narrow_cast<uint16_t>(remaining) });
}
if (max == _data._width || doubleBytePadded)
{
// TODO(DH): Evaluate the above condition
// We only want to do this if we touched or near-touched the lat col
SetDoubleBytePadded(doubleBytePadded);
SetWrapForced(false);
}
return charsFitOrRemain;
}
// Method Description:
// - get delimiter class for a position in the char row
// - used for double click selection and uia word navigation
// Arguments:
// - column: column to get text data for
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
// Return Value:
// - the delimiter class for the given char
const DelimiterClass DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const
{
THROW_HR_IF(E_INVALIDARG, column >= _data._width);
const auto glyph = *GlyphAt(column).begin();
if (glyph <= UNICODE_SPACE)
{
return DelimiterClass::ControlChar;
}
else if (wordDelimiters.find(glyph) != std::wstring_view::npos)
{
return DelimiterClass::DelimiterChar;
}
else
{
return DelimiterClass::RegularChar;
}
}
RowImage Dump(uint16_t left, uint16_t size)
{
auto [begin, len, min, max] = _data._damageForColumns(left, size);
return RowImage{
_data._data.substr(begin, len),
_data._cwid.slice(begin, begin + len),
ATTR_ROW{ _data._attrRow._data.slice(min, max) },
::base::MakeClampedNum(max) - min
};
}
void Reinsert(uint16_t left, const RowImage& ri)
{
auto damage{ _data._damageForColumns(left, ri._width) };
_data._strikeDamageAndAdjust(left, ri._width, ri._data.size(), damage);
_data._data.replace(damage.dataOffset, damage.dataLength, ri._data);
_data._cwid.replace(damage.dataOffset, damage.dataOffset + damage.dataLength, ri._cwid.runs());
_data._attrRow._data.replace(damage.firstColumn, damage.lastColumnExclusive, ri._attrRow._data.runs());
}
uint16_t _maxc{};
size_t MeasureRight() const
{
return _maxc;
}
};
#ifdef UNIT_TESTING
constexpr bool operator==(const ROW& a, const ROW& b) noexcept
{
// comparison is only used in the tests; this should suffice.
return (a._pParent == b._pParent &&
a._id == b._id);
return (a._data == b._data &&
a._cwid == b._cwid &&
a._attrRow == b._attrRow &&
a._rowWidth == b._rowWidth &&
a._wrapForced == b._wrapForced &&
a._doubleBytePadded == b._doubleBytePadded);
}
#endif
#pragma warning(pop)

View File

@@ -1,98 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "UnicodeStorage.hpp"
UnicodeStorage::UnicodeStorage() noexcept :
_map{}
{
}
// Routine Description:
// - fetches the text associated with key
// Arguments:
// - key - the key into the storage
// Return Value:
// - the glyph data associated with key
// Note: will throw exception if key is not stored yet
const UnicodeStorage::mapped_type& UnicodeStorage::GetText(const key_type key) const
{
return _map.at(key);
}
// Routine Description:
// - stores glyph data associated with key.
// Arguments:
// - key - the key into the storage
// - glyph - the glyph data to store
void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph)
{
_map.insert_or_assign(key, glyph);
}
// Routine Description:
// - erases key and its associated data from the storage
// Arguments:
// - key - the key to remove
void UnicodeStorage::Erase(const key_type key) noexcept
{
_map.erase(key);
}
// Routine Description:
// - Remaps all of the stored items to new coordinate positions
// based on a bulk rearrangement of row IDs and potential row width resize.
// Arguments:
// - rowMap - A map of the old row IDs to the new row IDs.
// - width - The width of the new row. Remove any items that are beyond the row width.
// - Use nullopt if we're not resizing the width of the row, just renumbering the rows.
void UnicodeStorage::Remap(const std::unordered_map<SHORT, SHORT>& rowMap, const std::optional<SHORT> width)
{
// Make a temporary map to hold all the new row positioning
std::unordered_map<key_type, mapped_type> newMap;
// Walk through every stored item.
for (const auto& pair : _map)
{
// Extract the old coordinate position
const auto oldCoord = pair.first;
// Only try to short-circuit based on width if we were told it changed
// by being given a new width value.
if (width.has_value())
{
// Get the column ID
const auto oldColId = oldCoord.X;
// If the column index is at/beyond the row width, don't bother copying it to the new map.
if (oldColId >= width.value())
{
continue;
}
}
// Get the row ID from the position as that's what we need to remap
const auto oldRowId = oldCoord.Y;
// Use the mapping given to convert the old row ID to the new row ID
const auto mapIter = rowMap.find(oldRowId);
// If there's no mapping to a new row, don't bother copying it to the new map. The row is gone.
if (mapIter == rowMap.end())
{
continue;
}
const auto newRowId = mapIter->second;
// Generate a new coordinate with the same X as the old one, but a new Y value.
const auto newCoord = COORD{ oldCoord.X, newRowId };
// Put the adjusted coordinate into the map with the original value.
newMap.emplace(newCoord, pair.second);
}
// Swap into the stored map, free the temporary when we exit.
_map.swap(newMap);
}

View File

@@ -1,67 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- UnicodeStorage.hpp
Abstract:
- dynamic storage location for glyphs that can't normally fit in the output buffer
Author(s):
- Austin Diviness (AustDi) 02-May-2018
--*/
#pragma once
#include <vector>
#include <unordered_map>
#include <climits>
// std::unordered_map needs help to know how to hash a COORD
namespace std
{
template<>
struct hash<COORD>
{
// Routine Description:
// - hashes a coord. coord will be hashed by storing the x and y values consecutively in the lower
// bits of a size_t.
// Arguments:
// - coord - the coord to hash
// Return Value:
// - the hashed coord
constexpr size_t operator()(const COORD& coord) const noexcept
{
size_t retVal = coord.Y;
const size_t xCoord = coord.X;
retVal |= xCoord << (sizeof(coord.Y) * CHAR_BIT);
return retVal;
}
};
}
class UnicodeStorage final
{
public:
using key_type = typename COORD;
using mapped_type = typename std::vector<wchar_t>;
UnicodeStorage() noexcept;
const mapped_type& GetText(const key_type key) const;
void StoreGlyph(const key_type key, const mapped_type& glyph);
void Erase(const key_type key) noexcept;
void Remap(const std::unordered_map<SHORT, SHORT>& rowMap, const std::optional<SHORT> width);
private:
std::unordered_map<key_type, mapped_type> _map;
#ifdef UNIT_TESTING
friend class UnicodeStorageTests;
friend class TextBufferTests;
#endif
};

View File

@@ -13,7 +13,6 @@
<ClCompile Include="..\AttrRow.cpp" />
<ClCompile Include="..\cursor.cpp" />
<ClCompile Include="..\OutputCell.cpp" />
<ClCompile Include="..\OutputCellIterator.cpp" />
<ClCompile Include="..\OutputCellRect.cpp" />
<ClCompile Include="..\OutputCellView.cpp" />
<ClCompile Include="..\Row.cpp" />
@@ -23,22 +22,16 @@
<ClCompile Include="..\textBuffer.cpp" />
<ClCompile Include="..\textBufferCellIterator.cpp" />
<ClCompile Include="..\textBufferTextIterator.cpp" />
<ClCompile Include="..\CharRow.cpp" />
<ClCompile Include="..\CharRowCell.cpp" />
<ClCompile Include="..\CharRowCellReference.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\UnicodeStorage.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\AttrRow.hpp" />
<ClInclude Include="..\cursor.h" />
<ClInclude Include="..\DbcsAttribute.hpp" />
<ClInclude Include="..\ICharRow.hpp" />
<ClInclude Include="..\LineRendition.hpp" />
<ClInclude Include="..\OutputCell.hpp" />
<ClInclude Include="..\OutputCellIterator.hpp" />
<ClInclude Include="..\OutputCellRect.hpp" />
<ClInclude Include="..\OutputCellView.hpp" />
<ClInclude Include="..\Row.hpp" />
@@ -48,11 +41,7 @@
<ClInclude Include="..\textBuffer.hpp" />
<ClInclude Include="..\textBufferCellIterator.hpp" />
<ClInclude Include="..\textBufferTextIterator.hpp" />
<ClInclude Include="..\CharRow.hpp" />
<ClInclude Include="..\CharRowCell.hpp" />
<ClInclude Include="..\CharRowCellReference.hpp" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\UnicodeStorage.hpp" />
</ItemGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />

View File

@@ -5,7 +5,6 @@
#include "search.h"
#include "CharRow.hpp"
#include "textBuffer.hpp"
#include "../types/inc/Utf16Parser.hpp"
#include "../types/inc/GlyphWidth.hpp"

View File

@@ -41,10 +41,6 @@ SOURCES= \
..\textBuffer.cpp \
..\textBufferCellIterator.cpp \
..\textBufferTextIterator.cpp \
..\CharRow.cpp \
..\CharRowCell.cpp \
..\CharRowCellReference.cpp \
..\UnicodeStorage.cpp \
..\search.cpp \
INCLUDES= \

View File

@@ -4,11 +4,11 @@
#include "precomp.h"
#include "textBuffer.hpp"
#include "CharRow.hpp"
#include "../types/inc/utils.hpp"
#include "../types/inc/convert.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../types/inc/Utf16Parser.hpp"
#pragma hdrstop
@@ -35,7 +35,6 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
_currentAttributes{ defaultAttributes },
_cursor{ cursorSize, *this },
_storage{},
_unicodeStorage{},
_renderTarget{ renderTarget },
_size{},
_currentHyperlinkId{ 1 },
@@ -184,277 +183,6 @@ TextBufferCellIterator TextBuffer::GetCellDataAt(const COORD at, const Viewport
return TextBufferCellIterator(*this, at, limit);
}
//Routine Description:
// - Corrects and enforces consistent double byte character state (KAttrs line) within a row of the text buffer.
// - This will take the given double byte information and check that it will be consistent when inserted into the buffer
// at the current cursor position.
// - It will correct the buffer (by erasing the character prior to the cursor) if necessary to make a consistent state.
//Arguments:
// - dbcsAttribute - Double byte information associated with the character about to be inserted into the buffer
//Return Value:
// - True if it is valid to insert a character with the given double byte attributes. False otherwise.
bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribute)
{
// To figure out if the sequence is valid, we have to look at the character that comes before the current one
const COORD coordPrevPosition = _GetPreviousFromCursor();
ROW& prevRow = GetRowByOffset(coordPrevPosition.Y);
DbcsAttribute prevDbcsAttr;
try
{
prevDbcsAttr = prevRow.GetCharRow().DbcsAttrAt(coordPrevPosition.X);
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
return false;
}
bool fValidSequence = true; // Valid until proven otherwise
bool fCorrectableByErase = false; // Can't be corrected until proven otherwise
// Here's the matrix of valid items:
// N = None (single byte)
// L = Lead (leading byte of double byte sequence
// T = Trail (trailing byte of double byte sequence
// Prev Curr Result
// N N OK.
// N L OK.
// N T Fail, uncorrectable. Trailing byte must have had leading before it.
// L N Fail, OK with erase. Lead needs trailing pair. Can erase lead to correct.
// L L Fail, OK with erase. Lead needs trailing pair. Can erase prev lead to correct.
// L T OK.
// T N OK.
// T L OK.
// T T Fail, uncorrectable. New trailing byte must have had leading before it.
// Check for only failing portions of the matrix:
if (prevDbcsAttr.IsSingle() && dbcsAttribute.IsTrailing())
{
// N, T failing case (uncorrectable)
fValidSequence = false;
}
else if (prevDbcsAttr.IsLeading())
{
if (dbcsAttribute.IsSingle() || dbcsAttribute.IsLeading())
{
// L, N and L, L failing cases (correctable)
fValidSequence = false;
fCorrectableByErase = true;
}
}
else if (prevDbcsAttr.IsTrailing() && dbcsAttribute.IsTrailing())
{
// T, T failing case (uncorrectable)
fValidSequence = false;
}
// If it's correctable by erase, erase the previous character
if (fCorrectableByErase)
{
// Erase previous character into an N type.
try
{
prevRow.ClearColumn(coordPrevPosition.X);
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
return false;
}
// Sequence is now N N or N L, which are both okay. Set sequence back to valid.
fValidSequence = true;
}
return fValidSequence;
}
//Routine Description:
// - Call before inserting a character into the buffer.
// - This will ensure a consistent double byte state (KAttrs line) within the text buffer
// - It will attempt to correct the buffer if we're inserting an unexpected double byte character type
// and it will pad out the buffer if we're going to split a double byte sequence across two rows.
//Arguments:
// - dbcsAttribute - Double byte information associated with the character about to be inserted into the buffer
//Return Value:
// - true if we successfully prepared the buffer and moved the cursor
// - false otherwise (out of memory)
bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute)
{
// This function corrects most errors. If this is false, we had an uncorrectable one which
// older versions of conhost simply let pass by unflinching.
LOG_HR_IF(E_NOT_VALID_STATE, !(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
bool fSuccess = true;
// Now compensate if we don't have enough space for the upcoming double byte sequence
// We only need to compensate for leading bytes
if (dbcsAttribute.IsLeading())
{
const auto cursorPosition = GetCursor().GetPosition();
const auto lineWidth = GetLineWidth(cursorPosition.Y);
// If we're about to lead on the last column in the row, we need to add a padding space
if (cursorPosition.X == lineWidth - 1)
{
// set that we're wrapping for double byte reasons
auto& row = GetRowByOffset(cursorPosition.Y);
row.SetDoubleBytePadded(true);
// then move the cursor forward and onto the next row
fSuccess = IncrementCursor();
}
}
return fSuccess;
}
// Routine Description:
// - Writes cells to the output buffer. Writes at the cursor.
// Arguments:
// - givenIt - Iterator representing output cell data to write
// Return Value:
// - The final position of the iterator
OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt)
{
const auto& cursor = GetCursor();
const auto target = cursor.GetPosition();
const auto finalIt = Write(givenIt, target);
return finalIt;
}
// Routine Description:
// - Writes cells to the output buffer.
// Arguments:
// - givenIt - Iterator representing output cell data to write
// - target - the row/column to start writing the text to
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data
// Return Value:
// - The final position of the iterator
OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt,
const COORD target,
const std::optional<bool> wrap)
{
// Make mutable copy so we can walk.
auto it = givenIt;
// Make mutable target so we can walk down lines.
auto lineTarget = target;
// Get size of the text buffer so we can stay in bounds.
const auto size = GetSize();
// While there's still data in the iterator and we're still targeting in bounds...
while (it && size.IsInBounds(lineTarget))
{
// Attempt to write as much data as possible onto this line.
// NOTE: if wrap = true/false, we want to set the line's wrap to true/false (respectively) if we reach the end of the line
it = WriteLine(it, lineTarget, wrap);
// Move to the next line down.
lineTarget.X = 0;
++lineTarget.Y;
}
return it;
}
// Routine Description:
// - Writes one line of text to the output buffer.
// Arguments:
// - givenIt - The iterator that will dereference into cell data to insert
// - target - Coordinate targeted within output buffer
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data in the iterator.
// - limitRight - Optionally restrict the right boundary for writing (e.g. stop writing earlier than the end of line)
// Return Value:
// - The iterator, but advanced to where we stopped writing. Use to find input consumed length or cells written length.
OutputCellIterator TextBuffer::WriteLine(const OutputCellIterator givenIt,
const COORD target,
const std::optional<bool> wrap,
std::optional<size_t> limitRight)
{
// If we're not in bounds, exit early.
if (!GetSize().IsInBounds(target))
{
return givenIt;
}
// Get the row and write the cells
ROW& row = GetRowByOffset(target.Y);
const auto newIt = row.WriteCells(givenIt, target.X, wrap, limitRight);
// Take the cell distance written and notify that it needs to be repainted.
const auto written = newIt.GetCellDistance(givenIt);
const Viewport paint = Viewport::FromDimensions(target, { gsl::narrow<SHORT>(written), 1 });
_NotifyPaint(paint);
return newIt;
}
//Routine Description:
// - Inserts one codepoint into the buffer at the current cursor position and advances the cursor as appropriate.
//Arguments:
// - chars - The codepoint to insert
// - dbcsAttribute - Double byte information associated with the codepoint
// - bAttr - Color data associated with the character
//Return Value:
// - true if we successfully inserted the character
// - false otherwise (out of memory)
bool TextBuffer::InsertCharacter(const std::wstring_view chars,
const DbcsAttribute dbcsAttribute,
const TextAttribute attr)
{
// Ensure consistent buffer state for double byte characters based on the character type we're about to insert
bool fSuccess = _PrepareForDoubleByteSequence(dbcsAttribute);
if (fSuccess)
{
// Get the current cursor position
short const iRow = GetCursor().GetPosition().Y; // row stored as logical position, not array position
short const iCol = GetCursor().GetPosition().X; // column logical and array positions are equal.
// Get the row associated with the given logical position
ROW& Row = GetRowByOffset(iRow);
// Store character and double byte data
CharRow& charRow = Row.GetCharRow();
try
{
charRow.GlyphAt(iCol) = chars;
charRow.DbcsAttrAt(iCol) = dbcsAttribute;
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
return false;
}
// Store color data
fSuccess = Row.GetAttrRow().SetAttrToEnd(iCol, attr);
if (fSuccess)
{
// Advance the cursor
fSuccess = IncrementCursor();
}
}
return fSuccess;
}
//Routine Description:
// - Inserts one ucs2 codepoint into the buffer at the current cursor position and advances the cursor as appropriate.
//Arguments:
// - wch - The codepoint to insert
// - dbcsAttribute - Double byte information associated with the codepoint
// - bAttr - Color data associated with the character
//Return Value:
// - true if we successfully inserted the character
// - false otherwise (out of memory)
bool TextBuffer::InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr)
{
return InsertCharacter({ &wch, 1 }, dbcsAttribute, attr);
}
//Routine Description:
// - Finds the current row in the buffer (as indicated by the cursor position)
// and specifies that we have forced a line wrap on that row
@@ -606,7 +334,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Consol
const auto& currRow = GetRowByOffset(coordEndOfText.Y);
// The X position of the end of the valid text is the Right draw boundary (which is one beyond the final valid character)
coordEndOfText.X = gsl::narrow<short>(currRow.GetCharRow().MeasureRight()) - 1;
coordEndOfText.X = gsl::narrow<short>(currRow.MeasureRight()) - 1;
// If the X coordinate turns out to be -1, the row was empty, we need to search backwards for the real end of text.
const auto viewportTop = viewport.Top();
@@ -617,7 +345,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Consol
const auto& backupRow = GetRowByOffset(coordEndOfText.Y);
// We need to back up to the previous row if this line is empty, AND there are more rows
coordEndOfText.X = gsl::narrow<short>(backupRow.GetCharRow().MeasureRight()) - 1;
coordEndOfText.X = gsl::narrow<short>(backupRow.MeasureRight()) - 1;
fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop);
}
@@ -777,7 +505,6 @@ void TextBuffer::ScrollRows(const SHORT firstRow, const SHORT size, const SHORT
}
// Renumber the IDs now that we've rearranged where the rows sit within the buffer.
// Refreshing should also delegate to the UnicodeStorage to re-key all the stored unicode sequences (where applicable).
_RefreshRowIDs(std::nullopt);
}
@@ -814,13 +541,11 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition)
// And if it's no longer single width, the right half of the row should be erased.
if (lineRendition != LineRendition::SingleWidth)
{
const auto fillChar = L' ';
auto fillAttrs = GetCurrentAttributes();
fillAttrs.SetStandardErase();
const size_t fillOffset = GetLineWidth(rowIndex);
const size_t fillLength = GetSize().Width() - fillOffset;
const auto fillData = OutputCellIterator{ fillChar, fillAttrs, fillLength };
row.WriteCells(fillData, fillOffset, false);
FillWithCharacterAndAttributeLinear(til::point{ fillOffset, ::base::saturated_cast<size_t>(cursorPosition.Y) }, fillLength, UNICODE_SPACE, fillAttrs);
// We also need to make sure the cursor is clamped within the new width.
GetCursor().SetPosition(ClampPositionWithinLine(cursorPosition));
}
@@ -930,8 +655,7 @@ void TextBuffer::Reset()
}
// Now that we've tampered with the row placement, refresh all the row IDs.
// 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.
// Also take advantage of the row ID refresh loop to resize the rows in the X dimension.
_RefreshRowIDs(newSize.X);
// Update the cached size value
@@ -942,40 +666,16 @@ void TextBuffer::Reset()
return S_OK;
}
const UnicodeStorage& TextBuffer::GetUnicodeStorage() const noexcept
{
return _unicodeStorage;
}
UnicodeStorage& TextBuffer::GetUnicodeStorage() noexcept
{
return _unicodeStorage;
}
// Routine Description:
// - Method to help refresh all the Row IDs after manipulating the row
// by shuffling pointers around.
// - This will also update parent pointers that are stored in depth within the buffer
// (e.g. it will update CharRow parents pointing at Rows that might have been moved around)
// - Optionally takes a new row width if we're resizing to perform a resize operation and cleanup
// any high unicode (UnicodeStorage) runs while we're already looping through the rows.
// - Optionally takes a new row width if we're resizing to perform a resize operation
// Arguments:
// - newRowWidth - Optional new value for the row width.
void TextBuffer::_RefreshRowIDs(std::optional<SHORT> newRowWidth)
{
std::unordered_map<SHORT, SHORT> rowMap;
SHORT i = 0;
for (auto& it : _storage)
{
// Build a map so we can update Unicode Storage
rowMap.emplace(it.GetId(), i);
// Update the IDs
it.SetId(i++);
// Also update the char row parent pointers as they can get shuffled up in the rotates.
it.GetCharRow().UpdateParent(&it);
// Resize the rows in the X dimension if we have a new width
if (newRowWidth.has_value())
{
@@ -983,9 +683,6 @@ void TextBuffer::_RefreshRowIDs(std::optional<SHORT> newRowWidth)
THROW_IF_FAILED(it.Resize(newRowWidth.value()));
}
}
// Give the new mapping to Unicode Storage
_unicodeStorage.Remap(rowMap, newRowWidth);
}
void TextBuffer::_NotifyPaint(const Viewport& viewport) const
@@ -1004,27 +701,6 @@ ROW& TextBuffer::_GetFirstRow()
return GetRowByOffset(0);
}
// Routine Description:
// - Retrieves the row that comes before the given row.
// - Does not wrap around the screen buffer.
// Arguments:
// - The current row.
// Return Value:
// - reference to the previous row
// Note:
// - will throw exception if called with the first row of the text buffer
ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row)
{
int prevRowIndex = Row.GetId() - 1;
if (prevRowIndex < 0)
{
prevRowIndex = TotalRowCount() - 1;
}
THROW_HR_IF(E_FAIL, Row.GetId() == _firstRow);
return _storage.at(prevRowIndex);
}
// Method Description:
// - Retrieves this buffer's current render target.
// Arguments:
@@ -1046,7 +722,7 @@ Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget() noexcep
// - the delimiter class for the given char
const DelimiterClass TextBuffer::_GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const
{
return GetRowByOffset(pos.Y).GetCharRow().DelimiterClassAt(pos.X, wordDelimiters);
return GetRowByOffset(pos.Y).DelimiterClassAt(pos.X, wordDelimiters);
}
// Method Description:
@@ -2138,8 +1814,13 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
// Fetch the row and its "right" which is the last printable character.
const ROW& row = oldBuffer.GetRowByOffset(iOldRow);
const short cOldColsTotal = oldBuffer.GetLineWidth(iOldRow);
const CharRow& charRow = row.GetCharRow();
short iRight = gsl::narrow_cast<short>(charRow.MeasureRight());
short iRight = gsl::narrow_cast<short>(row.MeasureRight());
if (iRight == 0 && !row.WasWrapForced())
{
// don't beef it if iRight=0 on iterator
newBuffer.NewlineCursor();
continue;
}
// If we're starting a new row, try and preserve the line rendition
// from the row in the original buffer.
@@ -2174,33 +1855,26 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
}
}
// Loop through every character in the current row (up to
// the "right" boundary, which is one past the final valid
// character)
for (short iOldCol = 0; iOldCol < iRight; iOldCol++)
#if 0
OutputCellIterator it{ oldBuffer.GetCellDataAt({ 0, iOldRow }, Viewport::FromDimensions({ 0, iOldRow }, iRight, 1)) };
auto preCurPos{ newBuffer.GetCursor().GetPosition() };
const auto last = newBuffer.Write(it, preCurPos, true); // true - wrap if we don't fit!
const auto distance = last.GetCellDistance(it);
#endif
auto preCurPos{ newBuffer.GetCursor().GetPosition() };
const auto distance = 5;
if (iOldRow == cOldCursorPos.Y)
{
if (iOldCol == cOldCursorPos.X && iOldRow == cOldCursorPos.Y)
{
cNewCursorPos = newCursor.GetPosition();
fFoundCursorPos = true;
}
try
{
// TODO: MSFT: 19446208 - this should just use an iterator and the inserter...
const auto glyph = row.GetCharRow().GlyphAt(iOldCol);
const auto dbcsAttr = row.GetCharRow().DbcsAttrAt(iOldCol);
const auto textAttr = row.GetAttrRow().GetAttrByColumn(iOldCol);
if (!newBuffer.InsertCharacter(glyph, dbcsAttr, textAttr))
{
hr = E_OUTOFMEMORY;
break;
}
}
CATCH_RETURN();
cNewCursorPos = preCurPos;
// we started at (0, NewY) -- walk forward by the old position's X to figure out where we land in the new line
newBuffer.GetSize().MoveInBounds(cOldCursorPos.X, cNewCursorPos);
fFoundCursorPos = true;
}
newBuffer.GetSize().MoveInBounds(distance, preCurPos);
newBuffer.GetCursor().SetPosition(preCurPos);
// If we found the old row that the caller was interested in, set the
// out value of that parameter to the cursor's current Y position (the
// new location of the _end_ of that row in the buffer).
@@ -2232,7 +1906,7 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
// Only do so if we were not forced to wrap. If we did
// force a word wrap, then the existing line break was
// only because we ran out of space.
if (iRight < cOldColsTotal && !row.WasWrapForced())
if (distance < cOldColsTotal && !row.WasWrapForced())
{
if (iRight == cOldCursorPos.X && iOldRow == cOldCursorPos.Y)
{
@@ -2563,3 +2237,224 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c
PointTree result(std::move(intervals));
return result;
}
TextBuffer::WriteResult TextBuffer::FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute)
{
return _WriteRectangular(
region,
[&](WriteResult& result, auto&& at, auto&& size) {
auto& row = GetRowByOffset(at.y());
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attribute);
result.columnsWritten += size;
});
}
TextBuffer::WriteResult TextBuffer::FillWithAttributeLinear(til::point at, size_t count, const TextAttribute& attribute)
{
auto out = _WriteLinear(
at,
[&](WriteResult& result, auto&& at, auto&& size) -> bool {
const auto w{ ::base::ClampMin(::base::saturated_cast<uint16_t>(size), count) };
auto& row = GetRowByOffset(at.y());
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attribute);
result.columnsWritten += w.RawValue();
const Viewport paint = Viewport::FromDimensions(at, { w.Cast<SHORT>(), 1 });
_NotifyPaint(paint);
return (count -= w.RawValue()) > 0; // keep going if data remains
});
return out;
}
// TODO(DH): make this a member so that IsGlyphFullWidth can be a member ref
static std::tuple<RowMeasurementBuffer, uint16_t> _MeasureStringAndCountColumns(std::wstring_view string) //const noexcept
{
RowMeasurementBuffer measurements;
uint16_t colCount{};
while (!string.empty())
{
auto codepoint = Utf16Parser::ParseNext(string);
string = string.substr(codepoint.size());
uint8_t measurement{ IsGlyphFullWidth(codepoint) ? 2u : 1u };
measurements.replace(measurements.size(), measurements.size(), { measurement, gsl::narrow_cast<uint16_t>(1) });
if (codepoint.size() > 1)
{
measurements.replace(measurements.size(), measurements.size(), typename decltype(measurements)::rle_type{ uint8_t{ 0 }, gsl::narrow_cast<uint16_t>(codepoint.size() - 1) });
}
colCount += measurement;
}
return { std::move(measurements), colCount };
}
struct no_attributes
{
};
template<typename T>
TextBuffer::WriteResult TextBuffer::_WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, RowMeasurementBuffer measurements, [[maybe_unused]] const T& attributes)
{
uint16_t colCount = std::accumulate(
measurements.runs().cbegin(),
measurements.runs().cend(),
uint16_t{},
[](auto&& v, auto&& r) -> uint16_t { return v + (r.value * r.length); });
const auto out = _WriteLinear(
at,
[&](WriteResult& result, auto&& at, auto&& /* size */) {
auto& row = GetRowByOffset(at.y());
const auto [consumedWchar, consumedDestCols, consumedSourceCols] = row.WriteStringAtMeasured(at.x<uint16_t>(), colCount, string, measurements);
if constexpr (std::is_same<T, TextAttribute>::value)
{
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), consumedDestCols), attributes);
}
#if 0
else if constexpr (std::is_same<T, ATTR_ROW::rle_vector>::value)
{
// user has passed us a packed representation of attributes
// It is a programming error to pass one whose total length does
// not match the measurement buffer
const auto attrRunSlice{ attributes.slice(0, consumedSourceCols) };
attrRunSlice.resize_trailing_extent(consumedDestCols); // this may be very wrong. TODO(DH)
// intent: I believe that this will take the attribute from the cell before the one we had to cut off
// and then extend it to cover our empty column?
row.GetAttrRow()._data.replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), consumedSourceCols), attrRunSlice.runs());
}
#endif
const Viewport paint = Viewport::FromDimensions(at, { ::base::saturated_cast<SHORT>(consumedDestCols), 1 });
_NotifyPaint(paint);
colCount -= consumedSourceCols;
result.charactersRead += consumedWchar;
result.charactersWritten += consumedWchar;
result.columnsWritten += consumedDestCols;
result.columnsRead += consumedSourceCols;
// unchecked substr
//string = std::wstring_view{ &*string.begin() + consumedWchar, string.size() - consumedWchar };
//string = til::unchecked_substr(string, consumedWchar);
string = string.substr(consumedWchar);
measurements = measurements.slice(gsl::narrow_cast<uint16_t>(consumedWchar), measurements.size());
return !string.empty();
});
return out;
}
TextBuffer::WriteResult TextBuffer::WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, RowMeasurementBuffer measurements)
{
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), _currentAttributes);
}
TextBuffer::WriteResult TextBuffer::WriteStringLinearWithAttributes(const til::point& at, const std::wstring_view& string, const TextAttribute& attr)
{
auto [measurements, colCount] = _MeasureStringAndCountColumns(string);
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), attr);
}
TextBuffer::WriteResult TextBuffer::WriteStringLinearKeepAttributes(const til::point& at, const std::wstring_view& string)
{
auto [measurements, colCount] = _MeasureStringAndCountColumns(string);
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), no_attributes{});
}
template<typename T>
TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, [[maybe_unused]] const T& attr)
{
const uint8_t width{ IsGlyphFullWidth(character) ? uint8_t{ 2u } : uint8_t{ 1u } };
auto out = _WriteRectangular(
region,
[&](WriteResult& /* result */, auto&& at, auto&& size) {
auto& row = GetRowByOffset(at.y());
row.Fill(region.left(), size / width, character, width);
if constexpr (std::is_same<T, TextAttribute>::value)
{
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attr);
}
});
return out;
}
template<typename T>
TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, [[maybe_unused]] const T& attr)
{
const uint8_t width{ IsGlyphFullWidth(character) ? uint8_t{ 2u } : uint8_t{ 1u } };
return _WriteLinear(
at,
[&](WriteResult& result, auto&& at, auto&& size) -> bool {
// size is how many *columns* we have left to fill, k?
const auto w{ ::base::ClampMin(::base::saturated_cast<uint16_t>(size), count * width) };
auto& row = GetRowByOffset(at.y());
const auto consumedWchars = row.Fill(at.x(), count, character, width);
if constexpr (std::is_same<T, TextAttribute>::value)
{
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), w), attr);
}
result.charactersWritten += consumedWchars;
result.columnsWritten += w.RawValue();
const Viewport paint = Viewport::FromDimensions(at, { w.Cast<SHORT>(), 1 });
_NotifyPaint(paint);
return (count -= consumedWchars) > 0; // keep going if data remains
});
}
TextBuffer::WriteResult TextBuffer::FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character)
{
return _FillWithCharacterAndAttributeRectangular(region, character, no_attributes{});
}
TextBuffer::WriteResult TextBuffer::FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr)
{
return _FillWithCharacterAndAttributeRectangular(region, character, attr);
}
TextBuffer::WriteResult TextBuffer::FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character)
{
return _FillWithCharacterAndAttributeLinear(at, count, character, no_attributes{});
}
TextBuffer::WriteResult TextBuffer::FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const TextAttribute& attr)
{
return _FillWithCharacterAndAttributeLinear(at, count, character, attr);
}
TextBuffer::WriteResult TextBuffer::WriteRowImage(const til::point at, const RowImage& ri)
{
auto& row{ GetRowByOffset(at.y()) };
row.Reinsert(at.x<uint16_t>(), ri);
return {};
}
template<typename TLambda>
TextBuffer::WriteResult TextBuffer::_WriteRectangular(const til::rectangle& rect, TLambda&& thing)
{
WriteResult out{};
for (auto y{ rect.top() }; y < rect.bottom(); ++y)
{
thing(out, til::point{ rect.left(), y }, rect.width());
}
_NotifyPaint(Viewport::FromDimensions(rect.origin(), rect.size()));
return out;
}
template<typename TLambda>
TextBuffer::WriteResult TextBuffer::_WriteLinear(const til::point& start, TLambda&& thing)
{
auto point{ start };
WriteResult out{};
while (_size.IsInBounds(point) && thing(out, point, _size.Width() - point.x()))
{
point = { 0, point.y() };
};
return out;
}

View File

@@ -54,7 +54,6 @@ filling in the last row, and updating the screen.
#include "cursor.h"
#include "Row.hpp"
#include "TextAttribute.hpp"
#include "UnicodeStorage.hpp"
#include "../types/inc/Viewport.hpp"
#include "../buffer/out/textBufferCellIterator.hpp"
@@ -86,19 +85,6 @@ public:
TextBufferTextIterator GetTextDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
// Text insertion functions
OutputCellIterator Write(const OutputCellIterator givenIt);
OutputCellIterator Write(const OutputCellIterator givenIt,
const COORD target,
const std::optional<bool> wrap = true);
OutputCellIterator WriteLine(const OutputCellIterator givenIt,
const COORD target,
const std::optional<bool> setWrap = std::nullopt,
const std::optional<size_t> limitRight = std::nullopt);
bool InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
bool InsertCharacter(const std::wstring_view chars, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
bool IncrementCursor();
bool NewlineCursor();
@@ -136,9 +122,6 @@ public:
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept;
UnicodeStorage& GetUnicodeStorage() noexcept;
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false) const;
@@ -200,6 +183,30 @@ public:
void CopyPatterns(const TextBuffer& OtherBuffer);
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const size_t firstRow, const size_t lastRow) const;
struct WriteResult
{
size_t charactersWritten;
size_t charactersRead;
size_t columnsWritten;
size_t columnsRead;
};
WriteResult WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, RowMeasurementBuffer measurements);
WriteResult WriteStringLinearWithAttributes(const til::point& at, const std::wstring_view& string, const TextAttribute& attr);
WriteResult WriteStringLinearKeepAttributes(const til::point& at, const std::wstring_view& string);
WriteResult FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character);
WriteResult FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character);
WriteResult FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr);
WriteResult FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const TextAttribute& attr);
WriteResult FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute);
WriteResult FillWithAttributeLinear(const til::point at, size_t count, const TextAttribute& attribute);
WriteResult WriteRowImage(const til::point at, const RowImage& ri);
private:
void _UpdateSize();
Microsoft::Console::Types::Viewport _size;
@@ -210,9 +217,6 @@ private:
TextAttribute _currentAttributes;
// storage location for glyphs that can't fit into the buffer normally
UnicodeStorage _unicodeStorage;
std::unordered_map<uint16_t, std::wstring> _hyperlinkMap;
std::unordered_map<std::wstring, uint16_t> _hyperlinkCustomIdMap;
uint16_t _currentHyperlinkId;
@@ -230,12 +234,7 @@ private:
void _NotifyPaint(const Microsoft::Console::Types::Viewport& viewport) const;
// Assist with maintaining proper buffer state for Double Byte character sequences
bool _PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute);
bool _AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribute);
ROW& _GetFirstRow();
ROW& _GetPrevRowNoWrap(const ROW& row);
void _ExpandTextRow(SMALL_RECT& selectionRow) const;
@@ -247,6 +246,21 @@ private:
void _PruneHyperlinks();
template<typename TLambda>
WriteResult _WriteRectangular(const til::rectangle& rect, TLambda&& thing);
template<typename TLambda>
WriteResult _WriteLinear(const til::point& start, TLambda&& thing);
template<typename T>
WriteResult _FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const T& attr);
template<typename T>
WriteResult _FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const T& attr);
template<typename T>
WriteResult _WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, RowMeasurementBuffer measurements, const T& attributes);
std::unordered_map<size_t, std::wstring> _idsAndPatterns;
size_t _currentPatternId;

View File

@@ -5,7 +5,6 @@
#include "textBufferCellIterator.hpp"
#include "CharRow.hpp"
#include "textBuffer.hpp"
#include "../types/inc/convert.hpp"
#include "../types/inc/viewport.hpp"
@@ -165,9 +164,8 @@ TextBufferCellIterator& TextBufferCellIterator::operator+=(const ptrdiff_t& move
_attrIter += diff;
_view.UpdateTextAttribute(*_attrIter);
const CharRow& charRow = _pRow->GetCharRow();
_view.UpdateText(charRow.GlyphAt(newX));
_view.UpdateDbcsAttribute(charRow.DbcsAttrAt(newX));
_view.UpdateText(_pRow->GlyphAt(newX));
_view.UpdateDbcsAttribute(_pRow->DbcsAttrAt(newX));
_pos.X = newX;
}
else
@@ -326,8 +324,8 @@ const ROW* TextBufferCellIterator::s_GetRow(const TextBuffer& buffer, const COOR
// - Updates the internal view. Call after updating row, attribute, or positions.
void TextBufferCellIterator::_GenerateView()
{
_view = OutputCellView(_pRow->GetCharRow().GlyphAt(_pos.X),
_pRow->GetCharRow().DbcsAttrAt(_pos.X),
_view = OutputCellView(_pRow->GlyphAt(_pos.X),
_pRow->DbcsAttrAt(_pos.X),
*_attrIter,
TextAttributeBehavior::Stored);
}

View File

@@ -15,12 +15,12 @@ Author(s):
#pragma once
#include "CharRow.hpp"
#include "AttrRow.hpp"
#include "OutputCellView.hpp"
#include "../../types/inc/viewport.hpp"
class TextBuffer;
class ROW;
class TextBufferCellIterator
{
@@ -58,8 +58,8 @@ protected:
const ROW* _pRow;
ATTR_ROW::const_iterator _attrIter;
const TextBuffer& _buffer;
const Microsoft::Console::Types::Viewport _bounds;
std::reference_wrapper<const TextBuffer> _buffer;
Microsoft::Console::Types::Viewport _bounds;
bool _exceeded;
COORD _pos;

View File

@@ -5,7 +5,6 @@
#include "textBufferTextIterator.hpp"
#include "CharRow.hpp"
#include "Row.hpp"
#pragma hdrstop

View File

@@ -336,9 +336,7 @@ void CommandListPopup::_drawList()
size_t lStringLength = Width();
for (SHORT i = 0; i < Height(); ++i)
{
const OutputCellIterator spaces(UNICODE_SPACE, _attributes, lStringLength);
const auto result = _screenInfo.Write(spaces, WriteCoord);
lStringLength = result.GetCellDistance(spaces);
_screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(WriteCoord, Width(), UNICODE_SPACE, _attributes);
WriteCoord.Y += 1i16;
}
@@ -431,10 +429,7 @@ void CommandListPopup::_drawList()
TextAttribute inverted = _attributes;
inverted.Invert();
const OutputCellIterator it(inverted, lStringLength);
const auto done = _screenInfo.Write(it, WriteCoord);
lStringLength = done.GetCellDistance(it);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{ WriteCoord, til::size{ Width(), 1 } }, inverted);
}
WriteCoord.Y += 1;
@@ -529,21 +524,13 @@ void CommandListPopup::_updateHighlight(const SHORT OldCurrentCommand, const SHO
}
COORD WriteCoord;
WriteCoord.X = _region.Left + 1i16;
size_t lStringLength = Width();
WriteCoord.Y = _region.Top + 1i16 + OldCurrentCommand - TopIndex;
const OutputCellIterator it(_attributes, lStringLength);
const auto done = _screenInfo.Write(it, WriteCoord);
lStringLength = done.GetCellDistance(it);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{ WriteCoord, til::size{ Width(), 1 } }, _attributes);
// highlight new command
WriteCoord.Y = _region.Top + 1i16 + NewCurrentCommand - TopIndex;
// inverted attributes
TextAttribute inverted = _attributes;
inverted.Invert();
const OutputCellIterator itAttr(inverted, lStringLength);
const auto doneAttr = _screenInfo.Write(itAttr, WriteCoord);
lStringLength = done.GetCellDistance(itAttr);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{ WriteCoord, til::size{ Width(), 1 } }, inverted);
}

View File

@@ -9,8 +9,6 @@
#include "handle.h"
#include "misc.h"
#include "../buffer/out/CharRow.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/Viewport.hpp"
#include "../types/inc/convert.hpp"
@@ -91,10 +89,48 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
return E_INVALIDARG;
}
const OutputCellIterator it(attrs);
const auto done = screenInfo.Write(it, target);
//const OutputCellIterator it(attrs);
//const auto done = screenInfo.Write(it, target);
__debugbreak();
// TODO(DH) restore writing runs of attributes
////////////////
#if 0
static rle_container rle_encode(const basic_container_view& from)
{
if (from.empty())
{
return {};
}
used = done.GetCellDistance(it);
rle_container to;
value_type value = from.front();
size_type length = 0;
for (auto v : from)
{
if (v != value)
{
to.emplace_back(value, length);
value = v;
length = 0;
}
length++;
}
if (length)
{
to.emplace_back(value, length);
}
return to;
}
#endif
////////////////
used = 0;
//used = done.GetCellDistance(it);
return S_OK;
}
@@ -133,9 +169,8 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
try
{
OutputCellIterator it(chars);
const auto finished = screenInfo.Write(it, target);
used = finished.GetInputDistance(it);
const auto result = screenInfo.GetTextBuffer().WriteStringLinearKeepAttributes(target, chars);
used = result.charactersWritten;
}
CATCH_RETURN();
@@ -218,11 +253,8 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
try
{
TextAttribute useThisAttr(attribute);
const OutputCellIterator it(useThisAttr, lengthToWrite);
const auto done = screenBuffer.Write(it, startingCoordinate);
cellsModified = done.GetCellDistance(it);
const auto result = screenBuffer.GetTextBuffer().FillWithAttributeLinear(startingCoordinate, lengthToWrite, TextAttribute{ attribute });
cellsModified = result.columnsWritten;
if (screenBuffer.HasAccessibilityEventing())
{
@@ -279,12 +311,11 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
HRESULT hr = S_OK;
try
{
const OutputCellIterator it(character, lengthToWrite);
// when writing to the buffer, specifically unset wrap if we get to the last column.
// a fill operation should UNSET wrap in that scenario. See GH #1126 for more details.
const auto done = screenInfo.Write(it, startingCoordinate, false);
cellsModified = done.GetInputDistance(it);
// TODO(DH) maintain above fix to clear wrap ^^^
const auto result = screenInfo.GetTextBuffer().FillWithCharacterLinear(startingCoordinate, lengthToWrite, character);
cellsModified = result.columnsWritten;
// Notify accessibility
if (screenInfo.HasAccessibilityEventing())

View File

@@ -20,6 +20,7 @@
#include "../types/inc/Viewport.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/Utf16Parser.hpp"
#pragma hdrstop
using namespace Microsoft::Console::Types;
@@ -339,7 +340,9 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
COORD CursorPosition = cursor.GetPosition();
NTSTATUS Status = STATUS_SUCCESS;
SHORT XPosition;
WCHAR LocalBuffer[LOCAL_BUFFER_SIZE];
std::wstring local;
local.reserve(LOCAL_BUFFER_SIZE);
RowMeasurementBuffer measurements;
size_t TempNumSpaces = 0;
const bool fUnprocessed = WI_IsFlagClear(screenInfo.OutputMode, ENABLE_PROCESSED_OUTPUT);
const bool fWrapAtEOL = WI_IsFlagSet(screenInfo.OutputMode, ENABLE_WRAP_AT_EOL_OUTPUT);
@@ -351,6 +354,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
const size_t BufferSize = *pcb;
*pcb = 0;
const wchar_t* const base = pwchRealUnicode;
const wchar_t* lpString = pwchRealUnicode;
COORD coordScreenBufferSize = screenInfo.GetBufferSize().Dimensions();
@@ -387,8 +391,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// As an optimization, collect characters in buffer and print out all at once.
XPosition = cursor.GetPosition().X;
size_t i = 0;
wchar_t* LocalBufPtr = LocalBuffer;
while (*pcb < BufferSize && i < LOCAL_BUFFER_SIZE && XPosition < coordScreenBufferSize.X)
while (*pcb < BufferSize && XPosition < coordScreenBufferSize.X)
{
#pragma prefast(suppress : 26019, "Buffer is taken in multiples of 2. Validation is ok.")
const wchar_t Char = *lpString;
@@ -396,19 +399,21 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// WCL-NOTE: to be identical to lpString. They are incremented in lockstep, never separately, and lpString
// WCL-NOTE: is initialized from pwchRealUnicode.
const wchar_t RealUnicodeChar = *pwchRealUnicode;
const auto cbuf{ Utf16Parser::ParseNext(std::wstring_view{ pwchRealUnicode, (BufferSize / sizeof(WCHAR)) - static_cast<size_t>(pwchRealUnicode - base) }) };
if (IS_GLYPH_CHAR(RealUnicodeChar) || fUnprocessed)
{
// WCL-NOTE: This operates on a single code unit instead of a whole codepoint. It will mis-measure surrogate pairs.
if (IsGlyphFullWidth(Char))
if (IsGlyphFullWidth(cbuf))
{
if (i < (LOCAL_BUFFER_SIZE - 1) && XPosition < (coordScreenBufferSize.X - 1))
if (XPosition < (coordScreenBufferSize.X - 1))
{
*LocalBufPtr++ = Char;
local.append(cbuf);
measurements.append(uint8_t{ 2u });
// cursor adjusted by 2 because the char is double width
XPosition += 2;
i += 1;
pwchBuffer++;
i += cbuf.size();
pwchBuffer += cbuf.size();
}
else
{
@@ -417,11 +422,15 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
}
else
{
*LocalBufPtr = Char;
LocalBufPtr++;
local.append(cbuf);
measurements.append(uint8_t{ 1u });
XPosition++;
i++;
pwchBuffer++;
i += cbuf.size();
pwchBuffer += cbuf.size();
}
for (auto z = cbuf.size(); z > 1; --z)
{
measurements.append(uint8_t{ 0 });
}
}
else
@@ -459,11 +468,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
goto EndWhile;
}
for (ULONG j = 0; j < TabSize && i < LOCAL_BUFFER_SIZE; j++, i++)
{
*LocalBufPtr = UNICODE_SPACE;
LocalBufPtr++;
}
local.append(TabSize, UNICODE_SPACE);
pwchBuffer++;
break;
@@ -477,32 +482,30 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
if ((dwFlags & WC_PRINTABLE_CONTROL_CHARS) && (IS_CONTROL_CHAR(RealUnicodeChar)))
{
CtrlChar:
if (i < (LOCAL_BUFFER_SIZE - 1))
//if (i < (LOCAL_BUFFER_SIZE - 1))
{
// WCL-NOTE: We do not properly measure that there is space for two characters
// WCL-NOTE: left on the screen.
*LocalBufPtr = (WCHAR)'^';
LocalBufPtr++;
local.append(1, L'^');
XPosition++;
i++;
*LocalBufPtr = (WCHAR)(RealUnicodeChar + (WCHAR)'@');
LocalBufPtr++;
local.append(1, (WCHAR)(RealUnicodeChar + (WCHAR)'@'));
XPosition++;
i++;
pwchBuffer++;
}
else
{
goto EndWhile;
}
//else
//{
//goto EndWhile;
//}
}
else
{
if (Char == UNICODE_NULL)
{
*LocalBufPtr = UNICODE_SPACE;
local.append(1, UNICODE_SPACE);
}
else
{
@@ -513,11 +516,13 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
GetStringTypeW(CT_CTYPE1, &RealUnicodeChar, 1, &CharType);
if (WI_IsFlagSet(CharType, C1_CNTRL))
{
WCHAR ch{};
ConvertOutputToUnicode(gci.OutputCP,
(LPSTR)&RealUnicodeChar,
1,
LocalBufPtr,
&ch,
1);
local.append(1, ch);
}
else
{
@@ -525,20 +530,19 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// WCL-NOTE: 1. Normal characters are handled via the early check for IS_GLYPH_CHAR
// WCL-NOTE: 2. Control characters are handled via the CtrlChar label (if WC_PRINTABLE_CONTROL_CHARS is on)
// WCL-NOTE: And if they are control characters they will trigger the C1_CNTRL check above.
*LocalBufPtr = Char;
local.append(1, Char);
}
}
LocalBufPtr++;
XPosition++;
i++;
pwchBuffer++;
}
}
}
lpString++;
pwchRealUnicode++;
*pcb += sizeof(WCHAR);
lpString += cbuf.size();
pwchRealUnicode += cbuf.size();
*pcb += cbuf.size() * sizeof(WCHAR);
}
EndWhile:
if (i != 0)
@@ -553,8 +557,12 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
}
// line was wrapped if we're writing up to the end of the current row
OutputCellIterator it(std::wstring_view(LocalBuffer, i), Attributes);
const auto itEnd = screenInfo.Write(it);
//OutputCellIterator it(std::wstring_view{ local }, Attributes);
//const auto itEnd = screenInfo.Write(it);
measurements.resize_trailing_extent(gsl::narrow_cast<uint16_t>(local.size()));
auto result = screenInfo.GetTextBuffer().WriteMeasuredStringLinear(CursorPosition, local, measurements);
local.clear();
measurements.resize_trailing_extent(0);
// Notify accessibility
if (screenInfo.HasAccessibilityEventing())
@@ -564,7 +572,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
// The number of "spaces" or "cells" we have consumed needs to be reported and stored for later
// when/if we need to erase the command line.
TempNumSpaces += itEnd.GetCellDistance(it);
TempNumSpaces += result.columnsWritten; //itEnd.GetCellDistance(it);
// WCL-NOTE: We are using the "estimated" X position delta instead of the actual delta from
// WCL-NOTE: the iterator. It is not clear why. If they differ, the cursor ends up in the
// WCL-NOTE: wrong place (typically inside another character).
@@ -702,7 +710,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
try
{
screenInfo.Write(OutputCellIterator(UNICODE_SPACE, Attributes, 1), CursorPosition);
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(CursorPosition, 1, UNICODE_SPACE, Attributes);
Status = STATUS_SUCCESS;
}
CATCH_LOG();
@@ -720,7 +728,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
try
{
screenInfo.Write(OutputCellIterator(UNICODE_SPACE, Attributes, 1), CursorPosition);
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(CursorPosition, 1, UNICODE_SPACE, Attributes);
Status = STATUS_SUCCESS;
}
CATCH_LOG();
@@ -742,7 +750,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
try
{
screenInfo.Write(OutputCellIterator(UNICODE_SPACE, Attributes, 1), cursor.GetPosition());
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(cursor.GetPosition(), 1, UNICODE_SPACE, Attributes);
}
CATCH_LOG();
}
@@ -797,9 +805,8 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
try
{
const OutputCellIterator it(UNICODE_SPACE, Attributes, NumChars);
const auto done = screenInfo.Write(it, cursor.GetPosition());
NumChars = done.GetCellDistance(it);
const auto result = screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(cursor.GetPosition(), NumChars, UNICODE_SPACE, Attributes);
NumChars = result.columnsWritten;
}
CATCH_LOG();
@@ -850,22 +857,18 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
{
const COORD TargetPoint = cursor.GetPosition();
ROW& Row = textBuffer.GetRowByOffset(TargetPoint.Y);
const CharRow& charRow = Row.GetCharRow();
try
{
// If we're on top of a trailing cell, clear it and the previous cell.
if (charRow.DbcsAttrAt(TargetPoint.X).IsTrailing())
if (Row.DbcsAttrAt(TargetPoint.X).IsTrailing())
{
// Space to clear for 2 cells.
OutputCellIterator it(UNICODE_SPACE, 2);
// Back target point up one.
auto writeTarget = TargetPoint;
writeTarget.X--;
// Write 2 clear cells.
screenInfo.Write(it, writeTarget);
screenInfo.GetTextBuffer().FillWithCharacterLinear(writeTarget, 2, UNICODE_SPACE);
}
}
catch (...)

View File

@@ -228,7 +228,7 @@ void DeleteCommandLine(COOKED_READ_DATA& cookedReadData, const bool fUpdateField
try
{
cookedReadData.ScreenInfo().Write(OutputCellIterator(UNICODE_SPACE, CharsToWrite), coordOriginalCursor);
cookedReadData.ScreenInfo().GetTextBuffer().FillWithCharacterLinear(coordOriginalCursor, CharsToWrite, UNICODE_SPACE);
}
CATCH_LOG();

View File

@@ -109,11 +109,10 @@ void ConversionAreaInfo::SetAttributes(const TextAttribute& attr)
// Arguments:
// - text - Text to insert into the conversion area buffer
// - column - Column to start at (X position)
void ConversionAreaInfo::WriteText(const std::vector<OutputCell>& text,
void ConversionAreaInfo::WriteText(const RowImage& text,
const SHORT column)
{
gsl::span<const OutputCell> view(text.data(), text.size());
_screenBuffer->Write(view, { column, 0 });
_screenBuffer->GetTextBuffer().WriteRowImage(til::point{ column, 0 }, text);
}
// Routine Description:

View File

@@ -61,7 +61,7 @@ public:
void SetWindowInfo(const SMALL_RECT view) noexcept;
void Paint() const noexcept;
void WriteText(const std::vector<OutputCell>& text, const SHORT column);
void WriteText(const RowImage& text, const SHORT column);
void SetAttributes(const TextAttribute& attr);
const TextBuffer& GetTextBuffer() const noexcept;

View File

@@ -217,76 +217,60 @@ TextAttribute ConsoleImeInfo::s_RetrieveAttributeAt(const size_t pos,
// - colorArray - Array of color values provided by the IME.
// Return Value:
// - Vector of OutputCells where each one represents one cell of the output buffer.
std::vector<OutputCell> ConsoleImeInfo::s_ConvertToCells(const std::wstring_view text,
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray)
RowImage ConsoleImeInfo::s_ConvertToCells(std::wstring_view text,
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray)
{
RowImage e;
std::vector<OutputCell> cells;
// - Convert incoming wchar_t stream into UTF-16 units.
const auto glyphs = Utf16Parser::Parse(text);
// - Walk through all of the grouped up text, match up the correct attribute to it, and make a new cell.
size_t attributesUsed = 0;
for (const auto& parsedGlyph : glyphs)
size_t attributesUsed{};
while (!text.empty())
{
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
// Collect up attributes that apply to this glyph range.
auto drawingAttr = s_RetrieveAttributeAt(attributesUsed, attributes, colorArray);
attributesUsed++;
const auto glyph{ Utf16Parser::ParseNext(text) };
text = text.substr(glyph.size());
const uint8_t width{ gsl::narrow_cast<uint8_t>(IsGlyphFullWidth(glyph) ? 2u : 1u) };
// The IME gave us an attribute for every glyph position in a surrogate pair.
// But the only important information will be the cursor position.
// Check all additional attributes to see if the cursor resides on top of them.
for (size_t i = 1; i < glyph.size(); i++)
{
TextAttribute additionalAttr = s_RetrieveAttributeAt(attributesUsed, attributes, colorArray);
attributesUsed++;
if (additionalAttr.IsLeftVerticalDisplayed())
{
drawingAttr.SetLeftVerticalDisplayed(true);
}
if (additionalAttr.IsRightVerticalDisplayed())
{
drawingAttr.SetRightVerticalDisplayed(true);
}
// add glyph
e._data.append(glyph);
// register its width
e._cwid.append(width);
for (auto z{ glyph.size() }; z > 1; --z)
{ // add a trailing 0-width for every surrogate pair trail
e._cwid.append(0);
}
// We have to determine if the glyph range is 1 column or two.
// If it's full width, it's two, and we need to make sure we don't draw the cursor
// right down the middle of the character.
// Otherwise it's one column and we'll push it in with the default empty DbcsAttribute.
DbcsAttribute dbcsAttr;
if (IsGlyphFullWidth(glyph))
auto drawingAttr = s_RetrieveAttributeAt(attributesUsed++, attributes, colorArray);
if (width == 1 || !(drawingAttr.IsLeftVerticalDisplayed() || drawingAttr.IsRightVerticalDisplayed()))
{
auto leftHalfAttr = drawingAttr;
auto rightHalfAttr = drawingAttr;
// Don't draw lines in the middle of full width glyphs.
// If we need a right vertical, don't apply it to the left side of the character
if (leftHalfAttr.IsRightVerticalDisplayed())
for (auto z{ width }; z > 0; --z) // add an attribute for each cell covered
{
leftHalfAttr.SetRightVerticalDisplayed(false);
e._attrRow._data.append(drawingAttr);
}
dbcsAttr.SetLeading();
cells.emplace_back(glyph, dbcsAttr, leftHalfAttr);
dbcsAttr.SetTrailing();
// If we need a left vertical, don't apply it to the right side of the character
if (rightHalfAttr.IsLeftVerticalDisplayed())
{
rightHalfAttr.SetLeftVerticalDisplayed(false);
}
cells.emplace_back(glyph, dbcsAttr, rightHalfAttr);
}
else
{
cells.emplace_back(glyph, dbcsAttr, drawingAttr);
}
}
// the width was >1 and the attribute contained the left or the right cursor indicator; split them into [ ___ ] (left, middle, right)
auto first = drawingAttr;
first.SetRightVerticalDisplayed(false);
auto middle = drawingAttr;
middle.SetLeftVerticalDisplayed(false);
middle.SetRightVerticalDisplayed(false);
auto last = drawingAttr;
last.SetLeftVerticalDisplayed(false);
return cells;
e._attrRow._data.append(first);
for (auto z{ width }; z > 2; --z)
{
e._attrRow._data.append(middle);
}
e._attrRow._data.append(last);
}
e._width += width;
}
return e;
}
// Routine Description:
@@ -308,81 +292,55 @@ std::vector<OutputCell> ConsoleImeInfo::s_ConvertToCells(const std::wstring_view
// However, if text couldn't fit in our line (full-width character starting at the very last cell)
// then we will give back the same begin and update the position for the next call to try again.
// If the viewport is deemed too small, we'll skip past it and advance begin past the entire full-width character.
std::vector<OutputCell>::const_iterator ConsoleImeInfo::_WriteConversionArea(const std::vector<OutputCell>::const_iterator begin,
const std::vector<OutputCell>::const_iterator end,
COORD& pos,
const Microsoft::Console::Types::Viewport view,
SCREEN_INFORMATION& screenInfo)
void ConsoleImeInfo::_WriteConversionArea(const RowImage& cells,
COORD pos,
const Microsoft::Console::Types::Viewport view,
SCREEN_INFORMATION& screenInfo)
{
// The position in the viewport where we will start inserting cells for this conversion area
// NOTE: We might exit early if there's not enough space to fit here, so we take a copy of
// the original and increment it up front.
const auto insertionPos = pos;
// Advance the cursor position to set up the next call for success (insert the next conversion area
// at the beginning of the following line)
pos.X = view.Left();
pos.Y++;
// The index of the last column in the viewport. (view is inclusive)
const auto finalViewColumn = view.RightInclusive();
// The maximum number of cells we can insert into a line.
const auto lineWidth = finalViewColumn - insertionPos.X + 1; // +1 because view was inclusive
// The iterator to the beginning position to form our line
const auto lineBegin = begin;
// The total number of cells we could insert.
const auto size = end - begin;
FAIL_FAST_IF(size <= 0); // It's a programming error to have <= 0 cells to insert.
// The end is the smaller of the remaining number of cells or the amount of line cells we can write before
// hitting the right edge of the viewport
auto lineEnd = lineBegin + std::min(size, (ptrdiff_t)lineWidth);
// We must attempt to compensate for ending on a leading byte. We can't split a full-width character across lines.
// As such, if the last item is a leading byte, back the end up by one.
FAIL_FAST_IF(lineEnd <= lineBegin); // We should have at least 1 space we can back up.
// Get the last cell in the run and if it's a leading byte, move the end position back one so we don't
// try to insert it.
const auto lastCell = lineEnd - 1;
if (lastCell->DbcsAttr().IsLeading())
auto remain{ cells };
do
{
lineEnd--;
}
// The maximum number of cells we can insert into a line.
const auto lineWidth = finalViewColumn - pos.X + 1; // +1 because view was inclusive
// Copy out the substring into a vector.
const std::vector<OutputCell> lineVec(lineBegin, lineEnd);
auto [draw, left] = cells.split(::base::saturated_cast<uint16_t>(lineWidth));
// Add a conversion area to the internal state to hold this line.
THROW_IF_FAILED(_AddConversionArea());
// Add a conversion area to the internal state to hold this line.
THROW_IF_FAILED(_AddConversionArea());
// Get the added conversion area.
auto& area = ConvAreaCompStr.back();
// Get the added conversion area.
auto& area = ConvAreaCompStr.back();
// Write our text into the conversion area.
area.WriteText(lineVec, insertionPos.X);
area.WriteText(draw, pos.X);
// Set the viewport and positioning parameters for the conversion area to describe to the renderer
// the appropriate location to overlay this conversion area on top of the main screen buffer inside the viewport.
const SMALL_RECT region{ insertionPos.X, 0, gsl::narrow<SHORT>(insertionPos.X + lineVec.size() - 1), 0 };
area.SetWindowInfo(region);
area.SetViewPos({ 0 - view.Left(), insertionPos.Y - view.Top() });
// Set the viewport and positioning parameters for the conversion area to describe to the renderer
// the appropriate location to overlay this conversion area on top of the main screen buffer inside the viewport.
const SMALL_RECT region{ pos.X, 0, gsl::narrow<SHORT>(pos.X + draw._width - 1), 0 };
area.SetWindowInfo(region);
area.SetViewPos({ 0 - view.Left(), pos.Y - view.Top() });
// Make it visible and paint it.
area.SetHidden(false);
area.Paint();
// Make it visible and paint it.
area.SetHidden(false);
area.Paint();
// Notify accessibility that we have updated the text in this display region within the viewport.
if (screenInfo.HasAccessibilityEventing())
{
screenInfo.NotifyAccessibilityEventing(insertionPos.X, insertionPos.Y, gsl::narrow<SHORT>(insertionPos.X + lineVec.size() - 1), insertionPos.Y);
}
// Notify accessibility that we have updated the text in this display region within the viewport.
if (screenInfo.HasAccessibilityEventing())
{
screenInfo.NotifyAccessibilityEventing(pos.X, pos.Y, gsl::narrow<SHORT>(pos.X + draw._width - 1), pos.Y);
}
// Hand back the iterator representing the end of what we used to be fed into the beginning of the next call.
return lineEnd;
if (remain._data.empty())
break;
// Advance the cursor position to set up the next call for success (insert the next conversion area
// at the beginning of the following line)
pos.X = view.Left();
pos.Y++;
remain = std::move(left);
} while (true);
}
// Routine Description:
@@ -432,17 +390,7 @@ void ConsoleImeInfo::_WriteUndeterminedChars(const std::wstring_view text,
const auto view = screenInfo.GetViewport();
// Set cursor position relative to viewport
// Set up our iterators. We will walk through the entire set of cells from beginning to end.
// The first time, we will give the iterators as the whole span and the begin
// will be moved forward by the conversion area write to set up the next call.
auto begin = cells.cbegin();
const auto end = cells.cend();
// Write over and over updating the beginning iterator until we reach the end.
do
{
begin = _WriteConversionArea(begin, end, pos, view, screenInfo);
} while (begin < end);
_WriteConversionArea(cells, pos, view, screenInfo);
}
// Routine Description:

View File

@@ -22,6 +22,7 @@ Revision History:
#include "../buffer/out/OutputCell.hpp"
#include "../buffer/out/TextAttribute.hpp"
#include "../renderer/inc/FontInfo.hpp"
#include "../buffer/out/Row.hpp"
#include "../types/inc/viewport.hpp"
#include "conareainfo.h"
@@ -73,15 +74,14 @@ private:
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray);
static std::vector<OutputCell> s_ConvertToCells(const std::wstring_view text,
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray);
static RowImage s_ConvertToCells(std::wstring_view text,
const gsl::span<const BYTE> attributes,
const gsl::span<const WORD> colorArray);
std::vector<OutputCell>::const_iterator _WriteConversionArea(const std::vector<OutputCell>::const_iterator begin,
const std::vector<OutputCell>::const_iterator end,
COORD& pos,
const Microsoft::Console::Types::Viewport view,
SCREEN_INFORMATION& screenInfo);
void _WriteConversionArea(const RowImage& cells,
COORD pos,
const Microsoft::Console::Types::Viewport view,
SCREEN_INFORMATION& screenInfo);
bool _isSavedCursorVisible;

View File

@@ -1010,8 +1010,9 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
const auto charInfos = gsl::span<const CHAR_INFO>(subspan.data(), subspan.size());
// Make the iterator and write to the target position.
OutputCellIterator it(charInfos);
storageBuffer.Write(it, target);
__debugbreak();
//OutputCellIterator it(charInfos);
//storageBuffer.Write(it, target);
}
// Since we've managed to write part of the request, return the clamped part that we actually used.

View File

@@ -2206,8 +2206,9 @@ void DoSrvPrivateMoveToBottom(SCREEN_INFORMATION& screenInfo)
fillAttrs.SetStandardErase();
}
const auto fillData = OutputCellIterator{ fillChar, fillAttrs, fillLength };
screenInfo.Write(fillData, startPosition, false);
//const auto fillData = OutputCellIterator{ fillChar, fillAttrs, fillLength };
//screenInfo.Write(fillData, startPosition, false);
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(startPosition, fillLength, fillChar, fillAttrs);
// Notify accessibility
if (screenInfo.HasAccessibilityEventing())

View File

@@ -104,8 +104,9 @@ static void _CopyRectangle(SCREEN_INFORMATION& screenInfo,
do
{
const auto data = OutputCell(*screenInfo.GetCellDataAt(sourcePos));
screenInfo.Write(OutputCellIterator({ &data, 1 }), targetPos);
__debugbreak();
//const auto data = OutputCell(*screenInfo.GetCellDataAt(sourcePos));
//screenInfo.Write(OutputCellIterator({ &data, 1 }), targetPos);
source.WalkInBounds(sourcePos, walkDirection);
} while (target.WalkInBounds(targetPos, walkDirection));
@@ -339,8 +340,8 @@ void ScrollRegion(SCREEN_INFORMATION& screenInfo,
const SMALL_RECT scrollRectGiven,
const std::optional<SMALL_RECT> clipRectGiven,
const COORD destinationOriginGiven,
const wchar_t fillCharGiven,
const TextAttribute fillAttrsGiven)
wchar_t fillChar,
TextAttribute fillAttrs)
{
// ------ 1. PREP SOURCE ------
// Set up the source viewport.
@@ -380,15 +381,12 @@ void ScrollRegion(SCREEN_INFORMATION& screenInfo,
return;
}
// Determine the cell we will use to fill in any revealed/uncovered space.
// We generally use exactly what was given to us.
OutputCellIterator fillData(fillCharGiven, fillAttrsGiven);
// However, if the character is null and we were given a null attribute (represented as legacy 0),
// then we'll just fill with spaces and whatever the buffer's default colors are.
if (fillCharGiven == UNICODE_NULL && fillAttrsGiven == TextAttribute{ 0 })
if (fillChar == UNICODE_NULL && fillAttrs == TextAttribute{ 0 })
{
fillData = OutputCellIterator(UNICODE_SPACE, screenInfo.GetAttributes());
fillChar = UNICODE_SPACE;
fillAttrs = screenInfo.GetAttributes();
}
// ------ 4. PREP TARGET ------
@@ -447,10 +445,9 @@ void ScrollRegion(SCREEN_INFORMATION& screenInfo,
const auto remaining = Viewport::Subtract(fill, target);
// Apply the fill data to each of the viewports we're given here.
for (size_t i = 0; i < remaining.size(); i++)
for (const auto& view : remaining)
{
const auto& view = remaining.at(i);
screenInfo.WriteRect(fillData, view);
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeRectangular(til::rectangle{ view.Origin(), til::size{ view.Dimensions() } }, fillChar, fillAttrs);
// If we're scrolling an area that encompasses the full buffer width,
// then the filled rows should also have their line rendition reset.

View File

@@ -90,50 +90,46 @@ void Popup::_DrawBorder()
COORD WriteCoord;
WriteCoord.X = _region.Left;
WriteCoord.Y = _region.Top;
_screenInfo.Write(OutputCellIterator(_attributes, Width() + 2), WriteCoord);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(_region, _attributes);
// draw upper left corner
_screenInfo.Write(OutputCellIterator(UNICODE_BOX_DRAW_LIGHT_DOWN_AND_RIGHT, 1), WriteCoord);
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, 1, UNICODE_BOX_DRAW_LIGHT_DOWN_AND_RIGHT);
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, 1, UNICODE_BOX_DRAW_LIGHT_DOWN_AND_RIGHT);
// draw upper bar
WriteCoord.X += 1;
_screenInfo.Write(OutputCellIterator(UNICODE_BOX_DRAW_LIGHT_HORIZONTAL, Width()), WriteCoord);
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, Width(), UNICODE_BOX_DRAW_LIGHT_HORIZONTAL);
// draw upper right corner
WriteCoord.X = _region.Right;
_screenInfo.Write(OutputCellIterator(UNICODE_BOX_DRAW_LIGHT_DOWN_AND_LEFT, 1), WriteCoord);
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, 1, UNICODE_BOX_DRAW_LIGHT_DOWN_AND_LEFT);
for (SHORT i = 0; i < Height(); i++)
{
WriteCoord.Y += 1;
WriteCoord.X = _region.Left;
// fill attributes
_screenInfo.Write(OutputCellIterator(_attributes, Width() + 2), WriteCoord);
_screenInfo.Write(OutputCellIterator(UNICODE_BOX_DRAW_LIGHT_VERTICAL, 1), WriteCoord);
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, 1, UNICODE_BOX_DRAW_LIGHT_VERTICAL);
WriteCoord.X = _region.Right;
_screenInfo.Write(OutputCellIterator(UNICODE_BOX_DRAW_LIGHT_VERTICAL, 1), WriteCoord);
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, 1, UNICODE_BOX_DRAW_LIGHT_VERTICAL);
}
// Draw bottom line.
// Fill attributes of top line.
WriteCoord.X = _region.Left;
WriteCoord.Y = _region.Bottom;
_screenInfo.Write(OutputCellIterator(_attributes, Width() + 2), WriteCoord);
// Draw bottom left corner.
WriteCoord.X = _region.Left;
_screenInfo.Write(OutputCellIterator(UNICODE_BOX_DRAW_LIGHT_UP_AND_RIGHT, 1), WriteCoord);
WriteCoord.Y = _region.Bottom;
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, 1, UNICODE_BOX_DRAW_LIGHT_UP_AND_RIGHT);
// Draw lower bar.
WriteCoord.X += 1;
_screenInfo.Write(OutputCellIterator(UNICODE_BOX_DRAW_LIGHT_HORIZONTAL, Width()), WriteCoord);
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, Width(), UNICODE_BOX_DRAW_LIGHT_HORIZONTAL);
// draw lower right corner
WriteCoord.X = _region.Right;
_screenInfo.Write(OutputCellIterator(UNICODE_BOX_DRAW_LIGHT_UP_AND_LEFT, 1), WriteCoord);
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, 1, UNICODE_BOX_DRAW_LIGHT_UP_AND_LEFT);
}
// Routine Description:
@@ -151,9 +147,7 @@ void Popup::_DrawPrompt(const UINT id)
size_t lStringLength = Width();
for (SHORT i = 0; i < Height(); i++)
{
const OutputCellIterator it(UNICODE_SPACE, _attributes, lStringLength);
const auto done = _screenInfo.Write(it, WriteCoord);
lStringLength = done.GetCellDistance(it);
_screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(WriteCoord, lStringLength, UNICODE_SPACE, _attributes);
WriteCoord.Y += 1;
}

View File

@@ -9,7 +9,6 @@
#include "_output.h"
#include "misc.h"
#include "handle.h"
#include "../buffer/out/CharRow.hpp"
#include <cmath>
#include "../interactivity/inc/ServiceLocator.hpp"
@@ -2251,10 +2250,10 @@ void SCREEN_INFORMATION::SetViewport(const Viewport& newViewport,
// i.e. the current background color, but with no meta attributes set.
auto fillAttributes = GetAttributes();
fillAttributes.SetStandardErase();
auto fillPosition = COORD{ 0, _viewport.Top() };
auto fillLength = gsl::narrow_cast<size_t>(_viewport.Height() * GetBufferSize().Width());
auto fillData = OutputCellIterator{ fillAttributes, fillLength };
Write(fillData, fillPosition, false);
// TODO(DH): The original code moved Left to 0 and Right to Width
_textBuffer->FillWithAttributeRectangular(til::rectangle{ _viewport.Origin(), til::size{ _viewport.Dimensions() } }, fillAttributes);
// TODO(DH): THIS ALSO CLEARED THE WRAP ON ALL ROWS (!)
//Write(fillData, fillPosition, false);
// Also reset the line rendition for the erased rows.
_textBuffer->ResetLineRenditionRange(_viewport.Top(), _viewport.BottomExclusive());
@@ -2335,37 +2334,8 @@ OutputCellRect SCREEN_INFORMATION::ReadRect(const Viewport viewport) const
return result;
}
// Routine Description:
// - Writes cells to the output buffer at the cursor position.
// Arguments:
// - it - Iterator representing output cell data to write.
// Return Value:
// - the iterator at its final position
// Note:
// - will throw exception on error.
OutputCellIterator SCREEN_INFORMATION::Write(const OutputCellIterator it)
{
return _textBuffer->Write(it);
}
// Routine Description:
// - Writes cells to the output buffer.
// Arguments:
// - it - Iterator representing output cell data to write.
// - target - The position to start writing at
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data
// Return Value:
// - the iterator at its final position
// Note:
// - will throw exception on error.
OutputCellIterator SCREEN_INFORMATION::Write(const OutputCellIterator it,
const COORD target,
const std::optional<bool> wrap)
{
// NOTE: if wrap = true/false, we want to set the line's wrap to true/false (respectively) if we reach the end of the line
return _textBuffer->Write(it, target, wrap);
}
#if 0
TODO(DH)
// Routine Description:
// - This routine writes a rectangular region into the screen buffer.
// Arguments:
@@ -2389,6 +2359,7 @@ OutputCellIterator SCREEN_INFORMATION::WriteRect(const OutputCellIterator it,
return iter;
}
#endif
// Routine Description:
// - This routine writes a rectangular region into the screen buffer.
@@ -2402,6 +2373,10 @@ OutputCellIterator SCREEN_INFORMATION::WriteRect(const OutputCellIterator it,
void SCREEN_INFORMATION::WriteRect(const OutputCellRect& data,
const COORD location)
{
(void)data;
(void)location;
__debugbreak();
#if 0
for (size_t i = 0; i < data.Height(); i++)
{
const auto iter = data.GetRowIter(i);
@@ -2410,8 +2385,10 @@ void SCREEN_INFORMATION::WriteRect(const OutputCellRect& data,
point.X = location.X;
point.Y = location.Y + static_cast<short>(i);
_textBuffer->WriteLine(iter, point);
__debugbreak();
//_textBuffer->WriteLine(iter, point);
}
#endif
}
// Routine Description:

View File

@@ -135,14 +135,7 @@ public:
TextBufferTextIterator GetTextLineDataAt(const COORD at) const;
TextBufferTextIterator GetTextDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
OutputCellIterator Write(const OutputCellIterator it);
OutputCellIterator Write(const OutputCellIterator it,
const COORD target,
const std::optional<bool> wrap = true);
OutputCellIterator WriteRect(const OutputCellIterator it,
const Microsoft::Console::Types::Viewport viewport);
size_t WriteMeasuredStringLinear(std::wstring_view string, const RowMeasurementBuffer& measurements);
void WriteRect(const OutputCellRect& data,
const COORD location);

View File

@@ -388,25 +388,7 @@ void Selection::ColorSelection(const SMALL_RECT& srRect, const TextAttribute att
// Read selection rectangle, assumed already clipped to buffer.
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
COORD coordTargetSize;
coordTargetSize.X = CalcWindowSizeX(srRect);
coordTargetSize.Y = CalcWindowSizeY(srRect);
COORD coordTarget;
coordTarget.X = srRect.Left;
coordTarget.Y = srRect.Top;
// Now color the selection a line at a time.
for (; (coordTarget.Y < srRect.Top + coordTargetSize.Y); ++coordTarget.Y)
{
const size_t cchWrite = gsl::narrow<size_t>(coordTargetSize.X);
try
{
screenInfo.Write(OutputCellIterator(attr, cchWrite), coordTarget);
}
CATCH_LOG();
}
screenInfo.GetTextBuffer().FillWithAttributeRectangular(srRect, attr);
}
// Routine Description:

View File

@@ -1,514 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
#include "../../inc/consoletaeftemplates.hpp"
#include "../buffer/out/outputCellIterator.hpp"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR };
class OutputCellIteratorTests
{
CommonState* m_state;
TEST_CLASS(OutputCellIteratorTests);
TEST_METHOD(CharacterFillDoubleWidth)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const wchar_t wch = L'\x30a2'; // katakana A
const size_t limit = 5;
OutputCellIterator it(wch, limit);
OutputCellView expectedLead({ &wch, 1 },
DbcsAttribute(DbcsAttribute::Attribute::Leading),
InvalidTextAttribute,
TextAttributeBehavior::Current);
OutputCellView expectedTrail({ &wch, 1 },
DbcsAttribute(DbcsAttribute::Attribute::Trailing),
InvalidTextAttribute,
TextAttributeBehavior::Current);
for (size_t i = 0; i < limit; i++)
{
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expectedLead, *it);
it++;
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expectedTrail, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(CharacterFillLimited)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const wchar_t wch = L'Q';
const size_t limit = 5;
OutputCellIterator it(wch, limit);
OutputCellView expected({ &wch, 1 },
DbcsAttribute{},
InvalidTextAttribute,
TextAttributeBehavior::Current);
for (size_t i = 0; i < limit; i++)
{
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(CharacterFillUnlimited)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const wchar_t wch = L'Q';
OutputCellIterator it(wch);
OutputCellView expected({ &wch, 1 },
DbcsAttribute{},
InvalidTextAttribute,
TextAttributeBehavior::Current);
for (size_t i = 0; i < SHORT_MAX; i++)
{
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_TRUE(it);
}
TEST_METHOD(AttributeFillLimited)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const TextAttribute attr(FOREGROUND_RED | BACKGROUND_BLUE);
const size_t limit = 5;
OutputCellIterator it(attr, limit);
OutputCellView expected({},
{},
attr,
TextAttributeBehavior::StoredOnly);
for (size_t i = 0; i < limit; i++)
{
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(AttributeFillUnlimited)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const TextAttribute attr(FOREGROUND_RED | BACKGROUND_BLUE);
OutputCellIterator it(attr);
OutputCellView expected({},
{},
attr,
TextAttributeBehavior::StoredOnly);
for (size_t i = 0; i < SHORT_MAX; i++)
{
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_TRUE(it);
}
TEST_METHOD(TextAndAttributeFillLimited)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const wchar_t wch = L'Q';
const TextAttribute attr(FOREGROUND_RED | BACKGROUND_BLUE);
const size_t limit = 5;
OutputCellIterator it(wch, attr, limit);
OutputCellView expected({ &wch, 1 },
{},
attr,
TextAttributeBehavior::Stored);
for (size_t i = 0; i < limit; i++)
{
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(TextAndAttributeFillUnlimited)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const wchar_t wch = L'Q';
const TextAttribute attr(FOREGROUND_RED | BACKGROUND_BLUE);
OutputCellIterator it(wch, attr);
OutputCellView expected({ &wch, 1 },
{},
attr,
TextAttributeBehavior::Stored);
for (size_t i = 0; i < SHORT_MAX; i++)
{
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_TRUE(it);
}
TEST_METHOD(CharInfoFillLimited)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
CHAR_INFO ci;
ci.Char.UnicodeChar = L'Q';
ci.Attributes = FOREGROUND_RED | BACKGROUND_BLUE;
const size_t limit = 5;
OutputCellIterator it(ci, limit);
OutputCellView expected({ &ci.Char.UnicodeChar, 1 },
{},
TextAttribute(ci.Attributes),
TextAttributeBehavior::Stored);
for (size_t i = 0; i < limit; i++)
{
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(CharInfoFillUnlimited)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
CHAR_INFO ci;
ci.Char.UnicodeChar = L'Q';
ci.Attributes = FOREGROUND_RED | BACKGROUND_BLUE;
OutputCellIterator it(ci);
OutputCellView expected({ &ci.Char.UnicodeChar, 1 },
{},
TextAttribute(ci.Attributes),
TextAttributeBehavior::Stored);
for (size_t i = 0; i < SHORT_MAX; i++)
{
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_TRUE(it);
}
TEST_METHOD(StringData)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const std::wstring testText(L"The quick brown fox jumps over the lazy dog.");
OutputCellIterator it(testText);
for (const auto& wch : testText)
{
OutputCellView expected({ &wch, 1 },
{},
InvalidTextAttribute,
TextAttributeBehavior::Current);
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(FullWidthStringData)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const std::wstring testText(L"\x30a2\x30a3\x30a4\x30a5\x30a6");
OutputCellIterator it(testText);
for (const auto& wch : testText)
{
auto expected = OutputCellView({ &wch, 1 },
DbcsAttribute(DbcsAttribute::Attribute::Leading),
InvalidTextAttribute,
TextAttributeBehavior::Current);
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
expected = OutputCellView({ &wch, 1 },
DbcsAttribute(DbcsAttribute::Attribute::Trailing),
InvalidTextAttribute,
TextAttributeBehavior::Current);
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(StringDataWithColor)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const std::wstring testText(L"The quick brown fox jumps over the lazy dog.");
const TextAttribute color(FOREGROUND_GREEN | FOREGROUND_INTENSITY);
OutputCellIterator it(testText, color);
for (const auto& wch : testText)
{
OutputCellView expected({ &wch, 1 },
{},
color,
TextAttributeBehavior::Stored);
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(FullWidthStringDataWithColor)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const std::wstring testText(L"\x30a2\x30a3\x30a4\x30a5\x30a6");
const TextAttribute color(FOREGROUND_GREEN | FOREGROUND_INTENSITY);
OutputCellIterator it(testText, color);
for (const auto& wch : testText)
{
auto expected = OutputCellView({ &wch, 1 },
DbcsAttribute(DbcsAttribute::Attribute::Leading),
color,
TextAttributeBehavior::Stored);
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
expected = OutputCellView({ &wch, 1 },
DbcsAttribute(DbcsAttribute::Attribute::Trailing),
color,
TextAttributeBehavior::Stored);
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(LegacyColorDataRun)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const std::vector<WORD> colors{ FOREGROUND_GREEN, FOREGROUND_RED | BACKGROUND_BLUE, FOREGROUND_BLUE | FOREGROUND_INTENSITY, BACKGROUND_GREEN };
const gsl::span<const WORD> view{ colors.data(), colors.size() };
OutputCellIterator it(view);
for (const auto& color : colors)
{
auto expected = OutputCellView({},
{},
TextAttribute{ color },
TextAttributeBehavior::StoredOnly);
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(LegacyCharInfoRun)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
std::vector<CHAR_INFO> charInfos;
for (auto i = 0; i < 5; i++)
{
CHAR_INFO ci;
ci.Char.UnicodeChar = static_cast<wchar_t>(L'A' + i);
ci.Attributes = gsl::narrow<WORD>(i);
charInfos.push_back(ci);
}
const gsl::span<const CHAR_INFO> view{ charInfos.data(), charInfos.size() };
OutputCellIterator it(view);
for (const auto& ci : charInfos)
{
auto expected = OutputCellView({ &ci.Char.UnicodeChar, 1 },
{},
TextAttribute{ ci.Attributes },
TextAttributeBehavior::Stored);
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(OutputCellRun)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
std::vector<OutputCell> cells;
for (auto i = 0; i < 5; i++)
{
const std::wstring pair(L"\xd834\xdd1e");
OutputCell cell(pair, {}, TextAttribute{ gsl::narrow<WORD>(i) });
cells.push_back(cell);
}
const gsl::span<const OutputCell> view{ cells.data(), cells.size() };
OutputCellIterator it(view);
for (const auto& cell : cells)
{
auto expected = OutputCellView(cell.Chars(),
cell.DbcsAttr(),
cell.TextAttr(),
cell.TextAttrBehavior());
VERIFY_IS_TRUE(it);
VERIFY_ARE_EQUAL(expected, *it);
it++;
}
VERIFY_IS_FALSE(it);
}
TEST_METHOD(DistanceStandard)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const std::wstring testText(L"The quick brown fox jumps over the lazy dog.");
OutputCellIterator it(testText);
const auto original = it;
ptrdiff_t expected = 0;
for (const auto& wch : testText)
{
wch; // unused
VERIFY_IS_TRUE(it);
it++;
expected++;
}
VERIFY_IS_FALSE(it);
VERIFY_ARE_EQUAL(expected, it.GetCellDistance(original));
VERIFY_ARE_EQUAL(expected, it.GetInputDistance(original));
}
TEST_METHOD(DistanceFullWidth)
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
const std::wstring testText(L"QWER\x30a2\x30a3\x30a4\x30a5\x30a6TYUI");
OutputCellIterator it(testText);
const auto original = it;
ptrdiff_t cellsExpected = 0;
ptrdiff_t inputExpected = 0;
for (const auto& wch : testText)
{
wch; // unused
VERIFY_IS_TRUE(it);
const auto value = *it;
it++;
if (value.DbcsAttr().IsLeading() || value.DbcsAttr().IsTrailing())
{
VERIFY_IS_TRUE(it);
it++;
cellsExpected++;
}
cellsExpected++;
inputExpected++;
}
VERIFY_IS_FALSE(it);
VERIFY_ARE_EQUAL(cellsExpected, it.GetCellDistance(original));
VERIFY_ARE_EQUAL(inputExpected, it.GetInputDistance(original));
}
};

View File

@@ -93,8 +93,6 @@ class TextBufferTests
TEST_METHOD(TestCopyProperties);
TEST_METHOD(TestInsertCharacter);
TEST_METHOD(TestIncrementCursor);
TEST_METHOD(TestNewlineCursor);
@@ -386,50 +384,6 @@ void TextBufferTests::TestCopyProperties()
VERIFY_IS_TRUE(testTextBuffer->GetCursor().GetDelay());
}
void TextBufferTests::TestInsertCharacter()
{
TextBuffer& textBuffer = GetTbi();
// get starting cursor position
COORD const coordCursorBefore = textBuffer.GetCursor().GetPosition();
// Get current row from the buffer
ROW& Row = textBuffer.GetRowByOffset(coordCursorBefore.Y);
// create some sample test data
const auto wch = L'Z';
const std::wstring_view wchTest(&wch, 1);
DbcsAttribute dbcsAttribute;
dbcsAttribute.SetTrailing();
WORD const wAttrTest = BACKGROUND_INTENSITY | FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE;
TextAttribute TestAttributes = TextAttribute(wAttrTest);
CharRow& charRow = Row.GetCharRow();
charRow.DbcsAttrAt(coordCursorBefore.X).SetLeading();
// ensure that the buffer didn't start with these fields
VERIFY_ARE_NOT_EQUAL(charRow.GlyphAt(coordCursorBefore.X), wchTest);
VERIFY_ARE_NOT_EQUAL(charRow.DbcsAttrAt(coordCursorBefore.X), dbcsAttribute);
auto attr = Row.GetAttrRow().GetAttrByColumn(coordCursorBefore.X);
VERIFY_ARE_NOT_EQUAL(attr, TestAttributes);
// now apply the new data to the buffer
textBuffer.InsertCharacter(wchTest, dbcsAttribute, TestAttributes);
// ensure that the buffer position where the cursor WAS contains the test items
VERIFY_ARE_EQUAL(charRow.GlyphAt(coordCursorBefore.X), wchTest);
VERIFY_ARE_EQUAL(charRow.DbcsAttrAt(coordCursorBefore.X), dbcsAttribute);
attr = Row.GetAttrRow().GetAttrByColumn(coordCursorBefore.X);
VERIFY_ARE_EQUAL(attr, TestAttributes);
// ensure that the cursor moved to a new position (X or Y or both have changed)
VERIFY_IS_TRUE((coordCursorBefore.X != textBuffer.GetCursor().GetPosition().X) ||
(coordCursorBefore.Y != textBuffer.GetCursor().GetPosition().Y));
// the proper advancement of the cursor (e.g. which position it goes to) is validated in other tests
}
void TextBufferTests::TestIncrementCursor()
{
TextBuffer& textBuffer = GetTbi();

View File

@@ -520,6 +520,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
_total_length = new_size;
}
void append(const value_type& value)
{
++_total_length;
if (!_runs.empty())
{
auto& back{ _runs.back() };
if (back.value == value)
{
++back.length;
return;
}
}
// Either runs was empty, or we have to append a new run.
_runs.emplace_back(value, S{ 1 });
}
constexpr bool operator==(const basic_rle& other) const noexcept
{
return _total_length == other._total_length && _runs == other._runs;

View File

@@ -23,7 +23,6 @@ Author(s):
#include "thread.hpp"
#include "../../buffer/out/textBuffer.hpp"
#include "../../buffer/out/CharRow.hpp"
namespace Microsoft::Console::Render
{

View File

@@ -26,7 +26,7 @@ namespace Microsoft::Console::Types
~Viewport() {}
constexpr Viewport() noexcept :
_sr({ 0, 0, -1, -1 }){};
Viewport(const Viewport& other) noexcept;
Viewport(const Viewport& other) = default;
Viewport(Viewport&&) = default;
Viewport& operator=(const Viewport&) & = default;
Viewport& operator=(Viewport&&) & = default;

View File

@@ -11,11 +11,6 @@ Viewport::Viewport(const SMALL_RECT sr) noexcept :
{
}
Viewport::Viewport(const Viewport& other) noexcept :
_sr(other._sr)
{
}
Viewport Viewport::Empty() noexcept
{
return Viewport();