Compare commits

...

28 Commits

Author SHA1 Message Date
Dustin L. Howett
6b2659bf65 Migrate spelling-0.0.21 changes from main 2021-04-27 09:27:31 -07:00
Dustin L. Howett
466b80496d Migrate spelling-0.0.19 changes from main 2021-04-27 09:27:31 -07:00
Michael Niksa
af2712b09e saving what I have 2021-04-27 09:27:31 -07:00
Michael Niksa
d2372342e2 npos 2021-03-25 13:50:01 -07:00
Michael Niksa
e59f2b8477 make spellcheck happy 2021-03-25 12:26:20 -07:00
Michael Niksa
60ef914956 Handful of PR feedback. 2021-03-25 12:25:26 -07:00
Michael Niksa
cdc5a6a747 Merge branch 'main' into dev/miniksa/rle 2021-03-24 11:02:08 -07:00
Don-Vito
da24f7d939 Allow overriding tab switcher mode on command level (#9507)
## Summary of the Pull Request

Currently, when the MRU is enabled we lose the keybinding allowing us to 
go forward/backward (aka right/left in LTR) in the tab view.

To fix that, this PR introduces "tabSwitcherMode" optional parameter to 
the prevTab / nextTab commands.
If it is not provided the global setting will be used.


So if you want to go to adjacent tabs, even if MRU is enabled on the
system level you can use:
```
{ "command": { "action": "prevTab", "tabSwitcherMode": "inOrder" }, "keys": "ctrl+f1"}
{ "command": { "action": "nextTab", "tabSwitcherMode": "inOrder" }, "keys": "ctrl+f2"}
```
or even
```
{"command": { "action": "prevTab", "tabSwitcherMode": "disabled" }, "keys": "ctrl+f1"}
{ "command": { "action": "nextTab", "tabSwitcherMode": "disabled" }, "keys": "ctrl+f2"}
```
if you don't want tab switcher to show up

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9330
* [x] CLA signed. 
* [x] Tests added/passed
* [ ] Documentation updated - not yet. Waiting for approval.
* [x] Schema updated.
* [ ] I've discussed this with core contributors already.
2021-03-23 22:00:07 +00:00
Dustin L. Howett
da3f02a6e2 Command Palette: announce various mode and state changes to UIA (#9582)
This commit introduces a few different announcements to the command
palette.

When you delete the `>`, it will announce that you have entered
"command-line mode". When you reintroduce the `>`, it will announce that
you are in "action search mode."

When you enter a nested command, it will announce that you are looking
at "more options for new tab..." or "more options for select color
scheme...".

When you search and find nothing, it will announce that there were no
matching commands (or tabs!)

Related to #7907.
2021-03-23 08:24:27 -05:00
Dustin L. Howett
d972ea2c28 Replace conhost's fmt::format strings with FMT_COMPILEd ones (#9581)
This reduces by 10% the binary size of OpenConsole x64 Release.

Note   | OpenConsole.exe
------ | ---------------------------
Before | 1156096
After  | 1037312
Delta  | -118784
%Delta | -10.27%
2021-03-23 02:21:18 +00:00
Don-Vito
a5ff7459b7 Prevent tab context menu from closing root pane directly (#9571)
## Summary of the Pull Request
Currently a repeated attempt to close a read-only tab from context menu,
will bring the terminal into invalid state if user dismisses close action.

There are two root causes for this:
1. The tab close menu triggers the closing of the root pane
(rather than invoking close tab flow in the Terminal Page).
2. Currently panes are not aware that the closing was canceled,
and thus they trigger the Closed event, putting the system in a weird state,
where the Closed handlers were invoked, but the Pane remains.

This PR mitigates #9502, by addressing the first root cause
(the fix is trivial and hopefully can be serviced).
Moreover, it addresses the only existing UI flow that can trigger the issue.

The remaining problematic flow will occur when the connection is closed.
I have created a separate Issue to track it: 
https://github.com/microsoft/terminal/issues/9572
as I guess the PR for it might be more complex.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9502
* [x] CLA signed. 
* [ ] Tests added/passed
* [ ] Documentation updated. 
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.
2021-03-22 20:48:18 +00:00
Dustin L. Howett
544bd44851 Update {fmt} to 7.1.3 (#9580)
{fmt} 7.1.3 includes a number of changes, which I will summarize here:

* Switched to Dragonbox for float formatting (quoted, "20-30x faster"
  than ostringstream)
* Significantly improves `FMT_COMPILE` (compile-time format string)
2021-03-22 13:07:04 -05:00
Don-Vito
9bd097f4ad Fix read-only tab dialog to cancel tab closing when dismissed (#9573)
Currently dismissing "are you sure you wish to close read-only tab or pane" 
dialog by pressing `ESC` will not abort tab closing
(aka the tab will be closed!)

The reason for this, is that we cancel, only if the "Cancel" is pressed
(aka result=PrimaryButton, while ESC returns result=None).

This PR fixes this, by doing what we usually do:
* Putting Cancel in the CloseButton (which is also triggered by ESC)
* Aborting the action if the result is not a Primary Button

However, since we want Cancel to be a default action,
we set CloseButton to be the DefaultButton  in XAML
2021-03-22 11:02:30 +00:00
Dustin L. Howett
9069fcab79 Remove the shell extension's dependency on fmt for huge savings (#9552)
Yeah, this one-line change shaves off almost half the binary.

Binary size savings (x64 Release):

Note   | WindowsTerminalShellExt.dll
------ | ---------------------------
Before | 130048
After  | 73728
Delta  | -56320
%Delta | -43.3%
2021-03-21 16:56:31 +00:00
Dustin L. Howett
f4d487efef github: migrate our bug report template to an issue form (#9538) 2021-03-19 12:03:43 -05:00
Michael Niksa
9b8c9d4f51 substring and hide to_string for non-unit-testing. 2021-01-15 15:30:19 -08:00
Michael Niksa
69538c4ab3 code format 2021-01-14 16:30:16 -08:00
Michael Niksa
324c0942c8 spell check 2021-01-14 16:29:33 -08:00
Michael Niksa
c65161d60e Let full batch assign through to public. Make AttrRow use it. TextAttributeRun is now just a std::pair<> so it fits nicely in the AttrRow which is just a til::rle now. Add tests. 2021-01-14 16:20:14 -08:00
Michael Niksa
b591355103 Drop attrrowtests.cpp. It overlaps/is obsoleted by RunLengthEncodingTests.cpp 2021-01-14 15:45:57 -08:00
Michael Niksa
a1c1d5cc09 Make AttrRow use til::rle directly. 2021-01-14 15:44:34 -08:00
Michael Niksa
67d591b11d Iterators and tests. 2021-01-14 14:45:47 -08:00
Michael Niksa
a692bb2f0f more rle 2021-01-13 15:58:08 -08:00
Michael Niksa
f00ffb0291 Get some tests going. 2021-01-12 16:25:33 -08:00
Michael Niksa
33e9a9b22a It compiles! 2021-01-11 15:50:10 -08:00
Michael Niksa
8114fa0dd0 it doesn't quite work yet, but it's what I'm going for. 2021-01-08 16:32:01 -08:00
Michael Niksa
00ca3f204c Clean out the rect stuff and prep for rle stuff. 2021-01-08 12:52:42 -08:00
Michael Niksa
1ed8ef0847 copy rectangle to rle and add to sources. 2021-01-08 12:48:15 -08:00
79 changed files with 7011 additions and 484113 deletions

View File

@@ -1,54 +0,0 @@
---
name: "Bug report 🐛"
about: Report errors or unexpected behavior
title: ''
labels: ''
assignees: ''
---
<!--
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.
All good? Then proceed!
-->
<!--
This bug tracker is monitored by Windows Terminal development team and other technical folks.
**Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to Github issues**.
Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue.
If this is an application crash, please also provide a Feedback Hub submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal (Preview)" and choose "Share My Feedback" after submission to get the link.
Please use this form and describe your issue, concisely but precisely, with as much detail as possible.
-->
# Environment
```none
Windows build number: [run `[Environment]::OSVersion` for powershell, or `ver` for cmd]
Windows Terminal version (if applicable):
Any other software?
```
# Steps to reproduce
<!-- A description of how to trigger this bug. -->
# Expected behavior
<!-- A description of what you're expecting, possibly containing screenshots or reference material. -->
# Actual behavior
<!-- What's actually happening? -->

57
.github/ISSUE_TEMPLATE/Bug_Report.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: "Bug report 🐛"
description: Report errors or unexpected behavior
title: ''
labels: ''
assignees: ''
issue_body: true
body:
- type: markdown
attributes:
value: |
Please make sure to [search for existing issues](https://github.com/microsoft/terminal/issues) before filing a new one!
If this is an application crash, please also provide a [Feedback Hub](https://aka.ms/terminal-feedback-hub) submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal" and choose "Share My Feedback" after submission to get the link.
- type: input
attributes:
label: Windows Terminal version (or Windows build number)
placeholder: "10.0.19042.0, 1.7.3651.0"
description: |
If you are reporting an issue in Windows Terminal, you can find the version in the about dialog.
If you are reporting an issue with the Windows Console, please run `ver` or `[Environment]::OSVersion`.
validations:
required: true
- type: textarea
attributes:
label: Other Software
description: If you're reporting a bug about our interaction with other software, what software? What versions?
placeholder: |
vim 8.2 (inside WSL)
OpenSSH_for_Windows_8.1p1
My Cool Application v0.3 (include a code snippet if it would help!)
validations:
required: false
- type: textarea
attributes:
label: Steps to reproduce
placeholder: Tell us the steps required to trigger your bug.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: If you want to include screenshots, paste them into the markdown editor below the form or follow up with a separate comment.
placeholder: What were you expecting?
validations:
required: false
- type: textarea
attributes:
label: Actual Behavior
placeholder: What happened instead?
validations:
required: true

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

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

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

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

View File

@@ -1,27 +0,0 @@
<details>
<summary>
:pencil2: Contributor please read this
</summary>
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
:warning: The command is written for posix shells. You can copy the contents of each `perl` command excluding the outer `'` marks and dropping any `'"`/`"'` quotation mark pairs into a file and then run `perl file.pl` from the root of the repository to run the code. Alternatively, you can manually insert the items...
If the listed items are:
* ... **misspelled**, then please *correct* them instead of using the command.
* ... *names*, please add them to `.github/actions/spelling/dictionary/names.txt`.
* ... APIs, you can add them to a file in `.github/actions/spelling/dictionary/`.
* ... just things you're using, please add them to an appropriate file in `.github/actions/spelling/expect/`.
* ... tokens you only need in one place and shouldn't *generally be used*, you can add an item in an appropriate file in `.github/actions/spelling/patterns/`.
See the `README.md` in each directory for more information.
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
:clamp: If you see a bunch of garbage and it relates to a binary-ish string, please add a file path to the `.github/actions/spelling/excludes.txt` file instead of just accepting the garbage.
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](https://github.com/microsoft/terminal/blob/main/README.md) (on whichever branch you're using).
</details>

View File

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

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

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

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

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

View File

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

View File

@@ -7,3 +7,4 @@ Iosevka
MDL
Monofur
Segoe
wght

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

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

View File

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

View File

@@ -1,24 +1,30 @@
Anup
austdi
arkthur
Ballmer
bhoj
Bhojwani
Bluloco
carlos
dhowett
Diviness
dsafa
duhowett
DXP
ekg
eryksun
ethanschoonover
Firefox
Gatta
glsl
Gravell
Grie
Griese
Hernan
Howett
Illhardt
iquilezles
italo
jantari
jerrysh
Kaiyu
@@ -30,8 +36,11 @@ Kourosh
kowalczyk
leonmsft
Lepilleur
lhecker
lukesampson
Macbook
Manandhar
masserano
mbadolato
Mehrain
menger
@@ -51,6 +60,7 @@ oldnewthing
opengl
osgwiki
pabhojwa
panos
paulcam
pauldotknopf
PGP
@@ -59,18 +69,23 @@ Rincewind
rprichard
Schoonover
shadertoy
Shomnipotence
simioni
Somuah
sonph
sonpham
stakx
talo
thereses
Walisch
WDX
Wellons
Wirt
Wojciech
zadjii
Zamor
zamora
Zamora
zamora
Zoey
zorio
Zverovich

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,120 +0,0 @@
ACCEPTFILES
ACCESSDENIED
alignof
bitfield
bitfields
CLASSNOTAVAILABLE
cmdletbinding
COLORPROPERTY
CXICON
CYICON
D2DERR_SHADER_COMPILE_FAILED
dataobject
DERR
environstrings
EXPCMDFLAGS
EXPCMDSTATE
FORCEMINIMIZE
frac
fullkbd
futex
GETDESKWALLPAPER
GETHIGHCONTRAST
Hashtable
HIGHCONTRASTON
HIGHCONTRASTW
href
IActivation
IApp
IAppearance
IAsync
IBind
IBox
IClass
IConnection
IComparable
ICustom
IDialog
IDirect
IExplorer
IInheritable
IMap
IObject
IPackage
IPeasant
IStorage
istream
IStringable
ITab
ITaskbar
IVirtual
LCID
llabs
llu
localtime
lround
LSHIFT
MULTIPLEUSE
msappx
MULTIPLEUSE
NCHITTEST
NCLBUTTONDBLCLK
NCRBUTTONDBLCLK
NOAGGREGATION
NOASYNC
NOPROGRESS
NOREDIRECTIONBITMAP
ntprivapi
oaidl
ocidl
otms
OUTLINETEXTMETRICW
overridable
PAGESCROLL
pmr
REGCLS
RETURNCMD
REGCLS
rfind
roundf
RSHIFT
rx
schandle
semver
serializer
shobjidl
SHOWMINIMIZED
SIZENS
smoothstep
GETDESKWALLPAPER
SHELLEXECUTEINFOW
snprintf
spsc
sregex
STDCPP
strchr
Subheader
Subpage
UPDATEINIFILE
syscall
TBPF
THEMECHANGED
tmp
tolower
TTask
TVal
tx
UPDATEINIFILE
userenv
wcsstr
wcstoui
wpc
wsregex
XDocument
XElement
xlocmes
xlocmon
xlocnum
xloctime
XParse
xstring

View File

@@ -1,782 +0,0 @@
snow
ghost-white
ghostwhite
white-smoke
whitesmoke
gainsboro
floral-white
floralwhite
old-lace
oldlace
linen
antique-white
antiquewhite
papaya-whip
papayawhip
blanched-almond
blanchedalmond
bisque
peach-puff
peachpuff
navajo-white
navajowhite
moccasin
cornsilk
ivory
lemon-chiffon
lemonchiffon
seashell
honeydew
mint-cream
mintcream
azure
alice-blue
aliceblue
lavender
lavender-blush
lavenderblush
misty-rose
mistyrose
white
black
dark-slate-gray
darkslategray
dark-slate-grey
darkslategrey
dim-gray
dimgray
dim-grey
dimgrey
slate-gray
slategray
slate-grey
slategrey
light-slate-gray
lightslategray
light-slate-grey
lightslategrey
gray
grey
xray
x11gray
xrey
x11grey
web-gray
webgray
web-grey
webgrey
light-grey
lightgrey
light-gray
lightgray
midnight-blue
midnightblue
navy
navy-blue
navyblue
cornflower-blue
cornflowerblue
dark-slate-blue
darkslateblue
slate-blue
slateblue
medium-slate-blue
mediumslateblue
light-slate-blue
lightslateblue
medium-blue
mediumblue
royal-blue
royalblue
blue
dodger-blue
dodgerblue
deep-sky-blue
deepskyblue
sky-blue
skyblue
light-sky-blue
lightskyblue
steel-blue
steelblue
light-steel-blue
lightsteelblue
light-blue
lightblue
powder-blue
powderblue
pale-turquoise
paleturquoise
dark-turquoise
darkturquoise
medium-turquoise
mediumturquoise
turquoise
cyan
aqua
light-cyan
lightcyan
cadet-blue
cadetblue
medium-aquamarine
mediumaquamarine
aquamarine
dark-green
darkgreen
dark-olive-green
darkolivegreen
dark-sea-green
darkseagreen
sea-green
seagreen
medium-sea-green
mediumseagreen
light-sea-green
lightseagreen
pale-green
palegreen
spring-green
springgreen
lawn-green
lawngreen
green
lime
xreen
x11green
web-green
webgreen
chartreuse
medium-spring-green
mediumspringgreen
green-yellow
greenyellow
lime-green
limegreen
yellow-green
yellowgreen
forest-green
forestgreen
olive-drab
olivedrab
dark-khaki
darkkhaki
khaki
pale-goldenrod
palegoldenrod
light-goldenrod-yellow
lightgoldenrodyellow
light-yellow
lightyellow
yellow
gold
light-goldenrod
lightgoldenrod
goldenrod
dark-goldenrod
darkgoldenrod
rosy-brown
rosybrown
indian-red
indianred
saddle-brown
saddlebrown
sienna
peru
burlywood
beige
wheat
sandy-brown
sandybrown
tan
chocolate
firebrick
brown
dark-salmon
darksalmon
salmon
light-salmon
lightsalmon
orange
dark-orange
darkorange
coral
light-coral
lightcoral
tomato
orange-red
orangered
red
hot-pink
hotpink
deep-pink
deeppink
pink
light-pink
lightpink
pale-violet-red
palevioletred
maroon
xaroon
x11maroon
web-maroon
webmaroon
medium-violet-red
mediumvioletred
violet-red
violetred
magenta
fuchsia
violet
plum
orchid
medium-orchid
mediumorchid
dark-orchid
darkorchid
dark-violet
darkviolet
blue-violet
blueviolet
purple
xurple
x11purple
web-purple
webpurple
medium-purple
mediumpurple
thistle
snow1
snow2
snow3
snow4
seashell1
seashell2
seashell3
seashell4
antiquewhite1
antiquewhite2
antiquewhite3
antiquewhite4
bisque1
bisque2
bisque3
bisque4
peachpuff1
peachpuff2
peachpuff3
peachpuff4
navajowhite1
navajowhite2
navajowhite3
navajowhite4
lemonchiffon1
lemonchiffon2
lemonchiffon3
lemonchiffon4
cornsilk1
cornsilk2
cornsilk3
cornsilk4
ivory1
ivory2
ivory3
ivory4
honeydew1
honeydew2
honeydew3
honeydew4
lavenderblush1
lavenderblush2
lavenderblush3
lavenderblush4
mistyrose1
mistyrose2
mistyrose3
mistyrose4
azure1
azure2
azure3
azure4
slateblue1
slateblue2
slateblue3
slateblue4
royalblue1
royalblue2
royalblue3
royalblue4
blue1
blue2
blue3
blue4
dodgerblue1
dodgerblue2
dodgerblue3
dodgerblue4
steelblue1
steelblue2
steelblue3
steelblue4
deepskyblue1
deepskyblue2
deepskyblue3
deepskyblue4
skyblue1
skyblue2
skyblue3
skyblue4
lightskyblue1
lightskyblue2
lightskyblue3
lightskyblue4
slategray1
slategray2
slategray3
slategray4
lightsteelblue1
lightsteelblue2
lightsteelblue3
lightsteelblue4
lightblue1
lightblue2
lightblue3
lightblue4
lightcyan1
lightcyan2
lightcyan3
lightcyan4
paleturquoise1
paleturquoise2
paleturquoise3
paleturquoise4
cadetblue1
cadetblue2
cadetblue3
cadetblue4
turquoise1
turquoise2
turquoise3
turquoise4
cyan1
cyan2
cyan3
cyan4
darkslategray1
darkslategray2
darkslategray3
darkslategray4
aquamarine1
aquamarine2
aquamarine3
aquamarine4
darkseagreen1
darkseagreen2
darkseagreen3
darkseagreen4
seagreen1
seagreen2
seagreen3
seagreen4
palegreen1
palegreen2
palegreen3
palegreen4
springgreen1
springgreen2
springgreen3
springgreen4
green1
green2
green3
green4
chartreuse1
chartreuse2
chartreuse3
chartreuse4
olivedrab1
olivedrab2
olivedrab3
olivedrab4
darkolivegreen1
darkolivegreen2
darkolivegreen3
darkolivegreen4
khaki1
khaki2
khaki3
khaki4
lightgoldenrod1
lightgoldenrod2
lightgoldenrod3
lightgoldenrod4
lightyellow1
lightyellow2
lightyellow3
lightyellow4
yellow1
yellow2
yellow3
yellow4
gold1
gold2
gold3
gold4
goldenrod1
goldenrod2
goldenrod3
goldenrod4
darkgoldenrod1
darkgoldenrod2
darkgoldenrod3
darkgoldenrod4
rosybrown1
rosybrown2
rosybrown3
rosybrown4
indianred1
indianred2
indianred3
indianred4
sienna1
sienna2
sienna3
sienna4
burlywood1
burlywood2
burlywood3
burlywood4
wheat1
wheat2
wheat3
wheat4
tan1
tan2
tan3
tan4
chocolate1
chocolate2
chocolate3
chocolate4
firebrick1
firebrick2
firebrick3
firebrick4
brown1
brown2
brown3
brown4
salmon1
salmon2
salmon3
salmon4
lightsalmon1
lightsalmon2
lightsalmon3
lightsalmon4
orange1
orange2
orange3
orange4
darkorange1
darkorange2
darkorange3
darkorange4
coral1
coral2
coral3
coral4
tomato1
tomato2
tomato3
tomato4
orangered1
orangered2
orangered3
orangered4
red1
red2
red3
red4
deeppink1
deeppink2
deeppink3
deeppink4
hotpink1
hotpink2
hotpink3
hotpink4
pink1
pink2
pink3
pink4
lightpink1
lightpink2
lightpink3
lightpink4
palevioletred1
palevioletred2
palevioletred3
palevioletred4
maroon1
maroon2
maroon3
maroon4
violetred1
violetred2
violetred3
violetred4
magenta1
magenta2
magenta3
magenta4
orchid1
orchid2
orchid3
orchid4
plum1
plum2
plum3
plum4
mediumorchid1
mediumorchid2
mediumorchid3
mediumorchid4
darkorchid1
darkorchid2
darkorchid3
darkorchid4
purple1
purple2
purple3
purple4
mediumpurple1
mediumpurple2
mediumpurple3
mediumpurple4
thistle1
thistle2
thistle3
thistle4
gray0
grey0
gray1
grey1
gray2
grey2
gray3
grey3
gray4
grey4
gray5
grey5
gray6
grey6
gray7
grey7
gray8
grey8
gray9
grey9
gray10
grey10
gray11
grey11
gray12
grey12
gray13
grey13
gray14
grey14
gray15
grey15
gray16
grey16
gray17
grey17
gray18
grey18
gray19
grey19
gray20
grey20
gray21
grey21
gray22
grey22
gray23
grey23
gray24
grey24
gray25
grey25
gray26
grey26
gray27
grey27
gray28
grey28
gray29
grey29
gray30
grey30
gray31
grey31
gray32
grey32
gray33
grey33
gray34
grey34
gray35
grey35
gray36
grey36
gray37
grey37
gray38
grey38
gray39
grey39
gray40
grey40
gray41
grey41
gray42
grey42
gray43
grey43
gray44
grey44
gray45
grey45
gray46
grey46
gray47
grey47
gray48
grey48
gray49
grey49
gray50
grey50
gray51
grey51
gray52
grey52
gray53
grey53
gray54
grey54
gray55
grey55
gray56
grey56
gray57
grey57
gray58
grey58
gray59
grey59
gray60
grey60
gray61
grey61
gray62
grey62
gray63
grey63
gray64
grey64
gray65
grey65
gray66
grey66
gray67
grey67
gray68
grey68
gray69
grey69
gray70
grey70
gray71
grey71
gray72
grey72
gray73
grey73
gray74
grey74
gray75
grey75
gray76
grey76
gray77
grey77
gray78
grey78
gray79
grey79
gray80
grey80
gray81
grey81
gray82
grey82
gray83
grey83
gray84
grey84
gray85
grey85
gray86
grey86
gray87
grey87
gray88
grey88
gray89
grey89
gray90
grey90
gray91
grey91
gray92
grey92
gray93
grey93
gray94
grey94
gray95
grey95
gray96
grey96
gray97
grey97
gray98
grey98
gray99
grey99
gray100
grey100
dark-grey
darkgrey
dark-gray
darkgray
dark-blue
darkblue
dark-cyan
darkcyan
dark-magenta
darkmagenta
dark-red
darkred
light-green
lightgreen
crimson
indigo
olive
rebecca-purple
rebeccapurple
silver
teal

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,27 +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$
@@ -29,38 +41,77 @@ SUMS$
\.lock$
\.map$
\.min\..
\.mod$
\.mp3$
\.mp4$
\.o$
\.ocf$
\.otf$
\.pbxproj$
\.pdf$
\.pem$
(?:(?i)\.png$)
\.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/
^oss/
^doc/reference/UTF8-torture-test\.txt$
^src/interactivity/onecore/BgfxEngine\.
^src/renderer/wddmcon/WddmConRenderer\.
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
^src/types/ut_types/UtilsTests.cpp$
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
^\.github/actions/spelling/
^\.gitignore$
^doc/reference/master-sequence-list.csv$
^doc/reference/UTF8-torture-test\.txt$
^oss/
^src/host/ft_uia/run\.bat$
^src/host/runft\.bat$
^src/host/runut\.bat$
^src/interactivity/onecore/BgfxEngine\.
^src/renderer/atlas/
^src/renderer/wddmcon/WddmConRenderer\.
^src/terminal/adapter/ut_adapter/run\.bat$
^src/terminal/parser/delfuzzpayload\.bat$
^src/terminal/parser/ft_fuzzer/run\.bat$
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
^src/terminal/parser/ft_fuzzwrapper/run\.bat$
^src/terminal/parser/ut_parser/Base64Test.cpp$
^src/terminal/parser/ut_parser/run\.bat$
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
^src/tools/lnkd/lnkd\.bat$
^src/tools/pixels/pixels\.bat$
^src/tools/texttests/fira\.txt$
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
^src/types/ut_types/UtilsTests.cpp$
^tools/ReleaseEngineering/ServicingPipeline.ps1$
ignore$
SUMS$

View File

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

File diff suppressed because it is too large Load Diff

View File

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

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

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

View File

@@ -1,15 +0,0 @@
name: Spell checking
on:
pull_request_target:
push:
jobs:
build:
name: Spell checking
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 5
- uses: check-spelling/check-spelling@0.0.17-alpha

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

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

View File

@@ -231,6 +231,19 @@
},
"type": "object"
},
"SwitchToAdjacentTabArgs" : {
"oneOf": [
{ "type": "null" },
{
"enum": [
"mru",
"inOrder",
"disabled"
],
"type": "string"
}
]
},
"ShortcutAction": {
"properties": {
"action": {
@@ -602,6 +615,38 @@
}
]
},
"PrevTabAction": {
"description": "Arguments corresponding to a Previous Tab Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type":"string", "pattern": "prevTab" },
"tabSwitcherMode": {
"$ref": "#/definitions/SwitchToAdjacentTabArgs",
"default": null,
"description": "Move to the previous tab using \"tabSwitcherMode\". If no mode is provided, use the one globally defined one."
}
}
}
]
},
"NextTabAction": {
"description": "Arguments corresponding to a Next Tab Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type":"string", "pattern": "nextTab" },
"tabSwitcherMode": {
"$ref": "#/definitions/SwitchToAdjacentTabArgs",
"default": null,
"description": "Move to the next tab using \"tabSwitcherMode\". If no mode is provided, use the one globally defined one."
}
}
}
]
},
"Keybinding": {
"additionalProperties": false,
"properties": {
@@ -628,6 +673,8 @@
{ "$ref": "#/definitions/MoveTabAction" },
{ "$ref": "#/definitions/FindMatchAction" },
{ "$ref": "#/definitions/NewWindowAction" },
{ "$ref": "#/definitions/NextTabAction" },
{ "$ref": "#/definitions/PrevTabAction" },
{ "type": "null" }
]
},

View File

@@ -4,7 +4,7 @@
"type": "git",
"git": {
"repositoryUrl": "https://github.com/fmtlib/fmt",
"commitHash": "f19b1a521ee8b606dedcadfda69fd10ddf882753"
"commitHash": "7bdf0628b1276379886c7f6dda2cef2b3b374f0b"
}
}
}

View File

@@ -72,43 +72,27 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
if (detail::const_check(F::is_signed && !T::is_signed)) {
// From may be negative, not allowed!
if (fmt::detail::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>((T::max)())) {
ec = 1;
return {};
}
if (F::digits > T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>((T::max)())) {
// outside range.
ec = 1;
return {};
}
}
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
return static_cast<To>(from); // Lossless conversion.
}
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
@@ -190,11 +174,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
if (ec) return {};
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
if (detail::const_check(Factor::num != 1)) {
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
@@ -209,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
return ec ? To() : To(tocount);
}
/**
@@ -351,6 +325,11 @@ inline std::tm localtime(std::time_t time) {
return lt.tm_;
}
inline std::tm localtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return localtime(std::chrono::system_clock::to_time_t(time_point));
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct dispatcher {
@@ -387,6 +366,11 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_;
}
inline std::tm gmtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return gmtime(std::chrono::system_clock::to_time_t(time_point));
}
namespace detail {
inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) {
@@ -399,6 +383,17 @@ inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
}
} // namespace detail
template <typename Char>
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
: formatter<std::tm, Char> {
template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock> val,
FormatContext& ctx) -> decltype(ctx.out()) {
std::tm time = localtime(val);
return formatter<std::tm, Char>::format(time, ctx);
}
};
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {

View File

@@ -463,16 +463,16 @@ template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
}
template <typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
}
template <typename Char>
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
@@ -496,7 +496,7 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
@@ -504,20 +504,22 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
}
/**
\rst
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
Example:
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
detail::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
@@ -558,7 +560,42 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return vformat(ts, to_string_view(format_str),
detail::make_args_checked<Args...>(format_str, args...));
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -368,7 +368,8 @@ template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
@@ -406,6 +407,19 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
return {{&s[pos], size}};
}
template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value);
}
};
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
@@ -430,7 +444,9 @@ template <typename Char, typename T, int N> struct spec_field {
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
basic_format_context<OutputIt, Char> ctx(out, {});
const auto& vargs =
make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(arg, ctx);
}
};
@@ -489,16 +505,17 @@ constexpr auto parse_tail(T head, S format_str) {
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos) {
size_t pos, int arg_id) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str);
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1};
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
}
// Compiles a non-empty format string and returns the compiled representation
@@ -518,8 +535,8 @@ constexpr auto compile_format_string(S format_str) {
format_str);
} else if constexpr (str[POS + 1] == ':') {
using type = get_type<ID, Args>;
constexpr auto result = parse_specs<type>(str, POS + 2);
return parse_tail<Args, result.end, ID + 1>(
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else {
return unknown_format();
@@ -530,8 +547,13 @@ constexpr auto compile_format_string(S format_str) {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
}
}
}
@@ -587,8 +609,7 @@ template <typename CompiledFormat, typename... Args,
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
basic_memory_buffer<Char> buffer;
detail::buffer<Char>& base = buffer;
cf.format(std::back_inserter(base), args...);
cf.format(detail::buffer_appender<Char>(buffer), args...);
return to_string(buffer);
}
@@ -608,8 +629,7 @@ template <typename CompiledFormat, typename... Args,
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::buffer<Char>& base = buffer;
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
@@ -618,9 +638,13 @@ template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
#ifdef __cpp_if_constexpr
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
}
#endif
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
@@ -643,18 +667,30 @@ OutputIt format_to(OutputIt out, const S&, const Args&... args) {
return format_to(out, compiled, args...);
}
template <
typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
detail::basic_compiled_format, CompiledFormat>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
template <typename OutputIt, typename CompiledFormat, typename... Args>
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
const Args&... args) ->
typename std::enable_if<
detail::is_output_iterator<OutputIt,
typename CompiledFormat::char_type>::value &&
std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value,
format_to_n_result<OutputIt>>::type {
auto it =
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(detail::counting_iterator(), cf, args...).count();

View File

@@ -18,7 +18,7 @@
#include <vector>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 70001
#define FMT_VERSION 70103
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
@@ -57,6 +57,7 @@
# define FMT_MSC_VER 0
# define FMT_SUPPRESS_MSC_WARNING(n)
#endif
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
#else
@@ -64,7 +65,7 @@
#endif
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
!(FMT_ICC_VERSION && FMT_ICC_VERSION < 1600)
(!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
# define FMT_HAS_INCLUDE(x) __has_include(x)
#else
# define FMT_HAS_INCLUDE(x) 0
@@ -99,7 +100,7 @@
#endif
#ifndef FMT_OVERRIDE
# if FMT_HAS_FEATURE(cxx_override) || \
# if FMT_HAS_FEATURE(cxx_override_control) || \
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
# define FMT_OVERRIDE override
# else
@@ -152,7 +153,7 @@
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
# define FMT_DEPRECATED [[deprecated]]
# else
# if defined(__GNUC__) || defined(__clang__)
# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
# define FMT_DEPRECATED __attribute__((deprecated))
# elif FMT_MSC_VER
# define FMT_DEPRECATED __declspec(deprecated)
@@ -177,9 +178,17 @@
# endif
#endif
#ifndef FMT_BEGIN_NAMESPACE
#ifndef FMT_USE_INLINE_NAMESPACES
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900
(FMT_MSC_VER >= 1900 && !_MANAGED)
# define FMT_USE_INLINE_NAMESPACES 1
# else
# define FMT_USE_INLINE_NAMESPACES 0
# endif
#endif
#ifndef FMT_BEGIN_NAMESPACE
# if FMT_USE_INLINE_NAMESPACES
# define FMT_INLINE_NAMESPACE inline namespace
# define FMT_END_NAMESPACE \
} \
@@ -269,8 +278,7 @@ struct monostate {};
namespace detail {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
// A helper function to suppress "conditional expression is constant" warnings.
template <typename T> constexpr T const_check(T value) { return value; }
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
@@ -299,7 +307,8 @@ template <typename T> struct std_string_view {};
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
!(FMT_CLANG_VERSION && FMT_MSC_VER)
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
@@ -506,6 +515,18 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
using type = typename result::value_type;
};
// Reports a compile-time error if S is not a valid format string.
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
FMT_INLINE void check_format_string(const S&) {
#ifdef FMT_ENFORCE_COMPILE_STRING
static_assert(is_compile_string<S>::value,
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
"FMT_STRING.");
#endif
}
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
struct error_handler {
constexpr error_handler() = default;
constexpr error_handler(const error_handler&) = default;
@@ -545,8 +566,9 @@ class basic_format_parse_context : private ErrorHandler {
using iterator = typename basic_string_view<Char>::iterator;
explicit constexpr basic_format_parse_context(
basic_string_view<Char> format_str, ErrorHandler eh = {})
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
basic_string_view<Char> format_str, ErrorHandler eh = {},
int next_arg_id = 0)
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
/**
Returns an iterator to the beginning of the format string range being
@@ -616,8 +638,24 @@ template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
// Checks whether T is a container with contiguous storage.
template <typename T> struct is_contiguous : std::false_type {};
template <typename Char>
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
namespace detail {
// Extracts a reference to the container from back_insert_iterator.
template <typename Container>
inline Container& get_container(std::back_insert_iterator<Container> it) {
using bi_iterator = std::back_insert_iterator<Container>;
struct accessor : bi_iterator {
accessor(bi_iterator iter) : bi_iterator(iter) {}
using bi_iterator::container;
};
return *accessor(it).container;
}
/**
\rst
A contiguous memory buffer with an optional growing ability. It is an internal
@@ -640,6 +678,8 @@ template <typename T> class buffer {
size_(sz),
capacity_(cap) {}
~buffer() = default;
/** Sets the buffer data and capacity. */
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
ptr_ = buf_data;
@@ -655,7 +695,6 @@ template <typename T> class buffer {
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
virtual ~buffer() = default;
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
@@ -675,24 +714,26 @@ template <typename T> class buffer {
/** Returns a pointer to the buffer data. */
const T* data() const FMT_NOEXCEPT { return ptr_; }
/**
Resizes the buffer. If T is a POD type new elements may not be initialized.
*/
void resize(size_t new_size) {
reserve(new_size);
size_ = new_size;
}
/** Clears this buffer. */
void clear() { size_ = 0; }
/** Reserves space to store at least *capacity* elements. */
void reserve(size_t new_capacity) {
// Tries resizing the buffer to contain *count* elements. If T is a POD type
// the new elements may not be initialized.
void try_resize(size_t count) {
try_reserve(count);
size_ = count <= capacity_ ? count : capacity_;
}
// Tries increasing the buffer capacity to *new_capacity*. It can increase the
// capacity by a smaller amount than requested but guarantees there is space
// for at least one additional element either by increasing the capacity or by
// flushing the buffer if it is full.
void try_reserve(size_t new_capacity) {
if (new_capacity > capacity_) grow(new_capacity);
}
void push_back(const T& value) {
reserve(size_ + 1);
try_reserve(size_ + 1);
ptr_[size_++] = value;
}
@@ -705,32 +746,150 @@ template <typename T> class buffer {
}
};
// A container-backed buffer.
struct buffer_traits {
explicit buffer_traits(size_t) {}
size_t count() const { return 0; }
size_t limit(size_t size) { return size; }
};
class fixed_buffer_traits {
private:
size_t count_ = 0;
size_t limit_;
public:
explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
size_t count() const { return count_; }
size_t limit(size_t size) {
size_t n = limit_ > count_ ? limit_ - count_ : 0;
count_ += size;
return size < n ? size : n;
}
};
// A buffer that writes to an output iterator when flushed.
template <typename OutputIt, typename T, typename Traits = buffer_traits>
class iterator_buffer final : public Traits, public buffer<T> {
private:
OutputIt out_;
enum { buffer_size = 256 };
T data_[buffer_size];
protected:
void grow(size_t) final FMT_OVERRIDE {
if (this->size() == buffer_size) flush();
}
void flush();
public:
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
: Traits(n),
buffer<T>(data_, 0, buffer_size),
out_(out) {}
~iterator_buffer() { flush(); }
OutputIt out() {
flush();
return out_;
}
size_t count() const { return Traits::count() + this->size(); }
};
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
protected:
void grow(size_t) final FMT_OVERRIDE {}
public:
explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
T* out() { return &*this->end(); }
};
// A buffer that writes to a container with the contiguous storage.
template <typename Container>
class container_buffer : public buffer<typename Container::value_type> {
class iterator_buffer<std::back_insert_iterator<Container>,
enable_if_t<is_contiguous<Container>::value,
typename Container::value_type>>
final : public buffer<typename Container::value_type> {
private:
Container& container_;
protected:
void grow(size_t capacity) FMT_OVERRIDE {
void grow(size_t capacity) final FMT_OVERRIDE {
container_.resize(capacity);
this->set(&container_[0], capacity);
}
public:
explicit container_buffer(Container& c)
explicit iterator_buffer(Container& c)
: buffer<typename Container::value_type>(c.size()), container_(c) {}
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
: iterator_buffer(get_container(out)) {}
std::back_insert_iterator<Container> out() {
return std::back_inserter(container_);
}
};
// Extracts a reference to the container from back_insert_iterator.
template <typename Container>
inline Container& get_container(std::back_insert_iterator<Container> it) {
using bi_iterator = std::back_insert_iterator<Container>;
struct accessor : bi_iterator {
accessor(bi_iterator iter) : bi_iterator(iter) {}
using bi_iterator::container;
};
return *accessor(it).container;
// A buffer that counts the number of code units written discarding the output.
template <typename T = char> class counting_buffer final : public buffer<T> {
private:
enum { buffer_size = 256 };
T data_[buffer_size];
size_t count_ = 0;
protected:
void grow(size_t) final FMT_OVERRIDE {
if (this->size() != buffer_size) return;
count_ += this->size();
this->clear();
}
public:
counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
size_t count() { return count_ + this->size(); }
};
// An output iterator that appends to the buffer.
// It is used to reduce symbol sizes for the common case.
template <typename T>
class buffer_appender : public std::back_insert_iterator<buffer<T>> {
using base = std::back_insert_iterator<buffer<T>>;
public:
explicit buffer_appender(buffer<T>& buf) : base(buf) {}
buffer_appender(base it) : base(it) {}
buffer_appender& operator++() {
base::operator++();
return *this;
}
buffer_appender operator++(int) {
buffer_appender tmp = *this;
++*this;
return tmp;
}
};
// Maps an output iterator into a buffer.
template <typename T, typename OutputIt>
iterator_buffer<OutputIt, T> get_buffer(OutputIt);
template <typename T> buffer<T>& get_buffer(buffer_appender<T>);
template <typename OutputIt> OutputIt get_buffer_init(OutputIt out) {
return out;
}
template <typename T> buffer<T>& get_buffer_init(buffer_appender<T> out) {
return get_container(out);
}
template <typename Buffer>
auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
return buf.out();
}
template <typename T> buffer_appender<T> get_iterator(buffer<T>& buf) {
return buffer_appender<T>(buf);
}
template <typename T, typename Char = char, typename Enable = void>
@@ -759,7 +918,8 @@ template <typename Char> struct named_arg_info {
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
struct arg_data {
// args_[0].named_args points to named_args_ to avoid bloating format_args.
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : 1)];
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
template <typename... U>
@@ -771,7 +931,8 @@ struct arg_data {
template <typename T, typename Char, size_t NUM_ARGS>
struct arg_data<T, Char, NUM_ARGS, 0> {
T args_[NUM_ARGS != 0 ? NUM_ARGS : 1];
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
template <typename... U>
FMT_INLINE arg_data(const U&... init) : args_{init...} {}
@@ -959,6 +1120,8 @@ enum { long_short = sizeof(long) == sizeof(int) };
using long_type = conditional_t<long_short, int, long long>;
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
struct unformattable {};
// Maps formatting arguments to core types.
template <typename Context> struct arg_mapper {
using char_type = typename Context::char_type;
@@ -1067,15 +1230,7 @@ template <typename Context> struct arg_mapper {
return map(val.value);
}
int map(...) {
constexpr bool formattable = sizeof(Context) == 0;
static_assert(
formattable,
"Cannot format argument. To make type T formattable provide a "
"formatter<T> specialization: "
"https://fmt.dev/latest/api.html#formatting-user-defined-types");
return 0;
}
unformattable map(...) { return {}; }
};
// A type constant after applying arg_mapper<Context>.
@@ -1199,15 +1354,25 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
return vis(monostate());
}
// Checks whether T is a container with contiguous storage.
template <typename T> struct is_contiguous : std::false_type {};
template <typename Char>
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
template <typename Char>
struct is_contiguous<detail::buffer<Char>> : std::true_type {};
template <typename T> struct formattable : std::false_type {};
namespace detail {
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
template <typename... Ts>
using void_t = typename detail::void_t_impl<Ts...>::type;
template <typename It, typename T, typename Enable = void>
struct is_output_iterator : std::false_type {};
template <typename It, typename T>
struct is_output_iterator<
It, T,
void_t<typename std::iterator_traits<It>::iterator_category,
decltype(*std::declval<It>() = std::declval<T>())>>
: std::true_type {};
template <typename OutputIt>
struct is_back_insert_iterator : std::false_type {};
template <typename Container>
@@ -1219,6 +1384,9 @@ struct is_contiguous_back_insert_iterator : std::false_type {};
template <typename Container>
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
: is_contiguous<Container> {};
template <typename Char>
struct is_contiguous_back_insert_iterator<buffer_appender<Char>>
: std::true_type {};
// A type-erased reference to an std::locale to avoid heavy <locale> include.
class locale_ref {
@@ -1250,13 +1418,24 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
return arg;
}
template <typename T> int check(unformattable) {
static_assert(
formattable<T>(),
"Cannot format an argument. To make type T formattable provide a "
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
return 0;
}
template <typename T, typename U> inline const U& check(const U& val) {
return val;
}
// The type template parameter is there to avoid an ODR violation when using
// a fallback formatter in one translation unit and an implicit conversion in
// another (not recommended).
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)>
inline value<Context> make_arg(const T& val) {
return arg_mapper<Context>().map(val);
return check<T>(arg_mapper<Context>().map(val));
}
template <bool IS_PACKED, typename Context, type, typename T,
@@ -1356,13 +1535,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
template <typename Char>
using buffer_context =
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
basic_format_context<detail::buffer_appender<Char>, Char>;
using format_context = buffer_context<char>;
using wformat_context = buffer_context<wchar_t>;
// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164.
// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
#define FMT_BUFFER_CONTEXT(Char) \
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>
basic_format_context<detail::buffer_appender<Char>, Char>
/**
\rst
@@ -1414,7 +1593,7 @@ class format_arg_store
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
Constructs a `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
can be omitted in which case it defaults to `~fmt::context`.
See `~fmt::arg` for lifetime considerations.
@@ -1426,6 +1605,27 @@ inline format_arg_store<Context, Args...> make_format_args(
return {args...};
}
/**
\rst
Constructs a `~fmt::format_arg_store` object that contains references
to arguments and can be implicitly converted to `~fmt::format_args`.
If ``format_str`` is a compile-time string then `make_args_checked` checks
its validity at compile time.
\endrst
*/
template <typename... Args, typename S, typename Char = char_t<S>>
inline auto make_args_checked(const S& format_str,
const remove_reference_t<Args>&... args)
-> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> {
static_assert(
detail::count<(
std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
std::is_reference<Args>::value)...>() == 0,
"passing views as lvalues is disallowed");
detail::check_format_string<Args...>(format_str);
return {args...};
}
/**
\rst
Returns a named argument to be used in a formatting function. It should only
@@ -1713,7 +1913,7 @@ template <typename Context> class basic_format_args {
}
template <typename Char> int get_id(basic_string_view<Char> name) const {
if (!has_named_args()) return {};
if (!has_named_args()) return -1;
const auto& named_args =
(is_packed() ? values_[-1] : args_[-1].value_).named_args;
for (size_t i = 0; i < named_args.size; ++i) {
@@ -1729,7 +1929,14 @@ template <typename Context> class basic_format_args {
}
};
/** An alias to ``basic_format_args<context>``. */
#ifdef FMT_ARM_ABI_COMPATIBILITY
/** An alias to ``basic_format_args<format_context>``. */
// Separate types would result in shorter symbols but break ABI compatibility
// between clang and gcc on ARM (#1919).
using format_args = basic_format_args<format_context>;
using wformat_args = basic_format_args<wformat_context>;
#else
// DEPRECATED! These are kept for ABI compatibility.
// It is a separate type rather than an alias to make symbols readable.
struct format_args : basic_format_args<format_context> {
template <typename... Args>
@@ -1738,32 +1945,10 @@ struct format_args : basic_format_args<format_context> {
struct wformat_args : basic_format_args<wformat_context> {
using basic_format_args::basic_format_args;
};
#endif
namespace detail {
// Reports a compile-time error if S is not a valid format string.
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
FMT_INLINE void check_format_string(const S&) {
#ifdef FMT_ENFORCE_COMPILE_STRING
static_assert(is_compile_string<S>::value,
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
"FMT_STRING.");
#endif
}
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
template <typename... Args, typename S, typename Char = char_t<S>>
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
make_args_checked(const S& format_str,
const remove_reference_t<Args>&... args) {
static_assert(count<(std::is_base_of<view, remove_reference_t<Args>>::value &&
std::is_reference<Args>::value)...>() == 0,
"passing views as lvalues is disallowed");
check_format_string<Args...>(format_str);
return {args...};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
std::basic_string<Char> vformat(
basic_string_view<Char> format_str,
@@ -1772,9 +1957,10 @@ std::basic_string<Char> vformat(
FMT_API std::string vformat(string_view format_str, format_args args);
template <typename Char>
typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
void vformat_to(
buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args);
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
detail::locale_ref loc = {});
template <typename Char, typename Args,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
@@ -1789,26 +1975,80 @@ inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
/** Formats a string and writes the output to ``out``. */
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
template <
typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
OutputIt vformat_to(
OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto& c = detail::get_container(out);
detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
template <typename OutputIt, typename S, typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value>
auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> typename std::enable_if<enable, OutputIt>::type {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
detail::vformat_to(buf, to_string_view(format_str), args);
return out;
return detail::get_iterator(buf);
}
template <typename Container, typename S, typename... Args,
FMT_ENABLE_IF(
is_contiguous<Container>::value&& detail::is_string<S>::value)>
inline std::back_insert_iterator<Container> format_to(
std::back_insert_iterator<Container> out, const S& format_str,
Args&&... args) {
return vformat_to(out, to_string_view(format_str),
detail::make_args_checked<Args...>(format_str, args...));
/**
\rst
Formats arguments, writes the result to the output iterator ``out`` and returns
the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out), "{}", 42);
\endrst
*/
// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3.
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const S& format_str, Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, to_string_view(format_str), vargs);
}
template <typename OutputIt> struct format_to_n_result {
/** Iterator past the end of the output range. */
OutputIt out;
/** Total (not truncated) output size. */
size_t size;
};
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline format_to_n_result<OutputIt> vformat_to_n(
OutputIt out, size_t n, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
/**
\rst
Formats arguments, writes up to ``n`` characters of the result to the output
iterator ``out`` and returns the total output size and the iterator past the
end of the output range.
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to_n(OutputIt out, size_t n, const S& format_str,
const Args&... args) ->
typename std::enable_if<enable, format_to_n_result<OutputIt>>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to_n(out, n, to_string_view(format_str), vargs);
}
/**
Returns the number of characters in the output of
``format(format_str, args...)``.
*/
template <typename... Args>
inline size_t formatted_size(string_view format_str, Args&&... args) {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
detail::counting_buffer<> buf;
detail::vformat_to(buf, format_str, vargs);
return buf.count();
}
template <typename S, typename Char = char_t<S>>
@@ -1832,7 +2072,7 @@ FMT_INLINE std::basic_string<Char> vformat(
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>>
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return detail::vformat(to_string_view(format_str), vargs);
}
@@ -1852,7 +2092,7 @@ FMT_API void vprint(std::FILE*, string_view, format_args);
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return detail::is_unicode<Char>()
? vprint(f, to_string_view(format_str), vargs)
: detail::vprint_mojibake(f, to_string_view(format_str), vargs);
@@ -1871,7 +2111,7 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(const S& format_str, Args&&... args) {
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return detail::is_unicode<Char>()
? vprint(to_string_view(format_str), vargs)
: detail::vprint_mojibake(stdout, to_string_view(format_str),

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -15,22 +15,12 @@
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
args, detail::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(loc, buffer, format_str, args);
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer);
}
} // namespace detail
@@ -45,32 +35,28 @@ inline std::basic_string<Char> vformat(
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return detail::vformat(
loc, to_string_view(format_str),
detail::make_args_checked<Args...>(format_str, args...));
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
format_args_t<type_identity_t<OutputIt>, Char> args) {
using af = detail::arg_formatter<OutputIt, Char>;
return vformat_to<af>(out, to_string_view(format_str), args,
detail::locale_ref(loc));
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
detail::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
detail::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
FMT_END_NAMESPACE

View File

@@ -29,7 +29,8 @@
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
@@ -278,7 +279,8 @@ class file {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT) // Create if the file doesn't exist.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
};
// Constructs a file object which doesn't represent any file.
@@ -343,36 +345,69 @@ class file {
// Returns the memory page size.
long getpagesize();
class direct_buffered_file;
namespace detail {
template <typename S, typename... Args>
void print(direct_buffered_file& f, const S& format_str,
const Args&... args);
struct buffer_size {
size_t value = 0;
buffer_size operator=(size_t val) const {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
// A buffered file with a direct buffer access and no synchronization.
class direct_buffered_file {
struct ostream_params {
int oflag = file::WRONLY | file::CREATE;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
template <typename... T>
ostream_params(T... params, int oflag) : ostream_params(params...) {
this->oflag = oflag;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
};
} // namespace detail
static constexpr detail::buffer_size buffer_size;
// A fast output stream which is not thread-safe.
class ostream final : private detail::buffer<char> {
private:
file file_;
enum { buffer_size = 4096 };
char buffer_[buffer_size];
int pos_;
void flush() {
if (pos_ == 0) return;
file_.write(buffer_, pos_);
pos_ = 0;
if (size() == 0) return;
file_.write(data(), size());
clear();
}
int free_capacity() const { return buffer_size - pos_; }
FMT_API void grow(size_t) override final;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public:
direct_buffered_file(cstring_view path, int oflag)
: file_(path, oflag), pos_(0) {}
~direct_buffered_file() {
flush();
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
@@ -380,25 +415,20 @@ class direct_buffered_file {
}
template <typename S, typename... Args>
friend void print(direct_buffered_file& f, const S& format_str,
const Args&... args) {
// We could avoid double buffering.
auto buf = fmt::memory_buffer();
fmt::format_to(std::back_inserter(buf), format_str, args...);
auto remaining_pos = 0;
auto remaining_size = buf.size();
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
auto size = f.free_capacity();
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
f.pos_ += size;
f.flush();
remaining_pos += size;
remaining_size -= size;
}
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
f.pos_ += static_cast<int>(remaining_size);
void print(const S& format_str, const Args&... args) {
format_to(detail::buffer_appender<char>(*this), format_str, args...);
}
};
/**
Opens a file for writing. Supported parameters passed in `params`:
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE

View File

@@ -49,17 +49,27 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
}
};
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
// Hide all operator<< from std::basic_ostream<Char>.
void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
@@ -103,7 +113,7 @@ void format_value(buffer<Char>& buf, const T& value,
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.resize(buf.size());
buf.try_resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
@@ -160,7 +170,7 @@ template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
detail::make_args_checked<Args...>(format_str, args...));
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -181,7 +181,7 @@ template <typename Char> class printf_width_handler {
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
Context(buffer_appender<Char>(buf), format, args).format();
}
} // namespace detail
@@ -598,7 +598,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;

View File

@@ -157,6 +157,9 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
@@ -182,7 +185,6 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace detail
template <typename T> struct is_tuple_like {
@@ -246,9 +248,18 @@ template <typename T, typename Char> struct is_range {
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
struct formatter<RangeT, Char,
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&&
(has_formatter<detail::value_type<T>, format_context>::value ||
detail::has_fallback_formatter<detail::value_type<T>,
format_context>::value)
#endif
>> {
formatting_range<Char> formatting;
template <typename ParseContext>
@@ -257,8 +268,7 @@ struct formatter<RangeT, Char,
}
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto it = values.begin();

View File

@@ -23,6 +23,36 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
FMT_NOEXCEPT;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
FMT_NOEXCEPT;
// DEPRECATED! This function exists for ABI compatibility.
template <typename Char>
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
Char>::iterator
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>>
args) {
using iterator = std::back_insert_iterator<buffer<char>>;
using context = basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>;
auto out = iterator(buf);
format_handler<iterator, Char, context> h(out, format_str, args, {});
parse_format_string<false>(format_str, h);
return out;
}
template basic_format_context<std::back_insert_iterator<buffer<char>>,
char>::iterator
vformat_to(buffer<char>&, string_view,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<char>>>,
type_identity_t<char>>>);
} // namespace detail
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
@@ -44,9 +74,9 @@ template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<char>::append(const char*, const char*);
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>);
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);

View File

@@ -62,7 +62,7 @@ using RWResult = int;
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using RWResult = ssize_t;
@@ -124,7 +124,8 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
format_to(std::back_inserter(out), "{}: {}", message, utf8_message);
format_to(buffer_appender<char>(out), "{}: {}", message,
utf8_message);
return;
}
break;
@@ -288,12 +289,12 @@ void file::pipe(file& read_end, file& write_end) {
}
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
#if defined(__MINGW32__) && defined(_POSIX_)
// Don't retry as fdopen doesn't return EINTR.
# if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
#else
# else
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
#endif
# endif
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
@@ -313,5 +314,9 @@ long getpagesize() {
return size;
# endif
}
FMT_API void ostream::grow(size_t) {
if (this->size() == this->capacity()) flush();
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

View File

@@ -4,36 +4,6 @@
#include "precomp.h"
#include "AttrRow.hpp"
// Routine Description:
// - constructor
// Arguments:
// - cchRowWidth - the length of the default text attribute
// - attr - the default text attribute
// Return Value:
// - constructed object
ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr) noexcept
{
try
{
_list.emplace_back(TextAttributeRun(cchRowWidth, attr));
}
catch (...)
{
FAIL_FAST_CAUGHT_EXCEPTION();
}
_cchRowWidth = cchRowWidth;
}
// Routine Description:
// - Sets all properties of the ATTR_ROW to default values
// Arguments:
// - attr - The default text attributes to use on text in this row.
void ATTR_ROW::Reset(const TextAttribute attr)
{
_list.clear();
_list.emplace_back(TextAttributeRun(_cchRowWidth, attr));
}
// Routine Description:
// - Takes an existing row of attributes, and changes the length so that it fills the NewWidth.
// If the new size is bigger, then the last attr is extended to fill the NewWidth.
@@ -45,46 +15,12 @@ void ATTR_ROW::Reset(const TextAttribute attr)
// - <none>, throws exceptions on failures.
void ATTR_ROW::Resize(const size_t newWidth)
{
THROW_HR_IF(E_INVALIDARG, 0 == newWidth);
mybase::resize(gsl::narrow<UINT>(newWidth));
}
// Easy case. If the new row is longer, increase the length of the last run by how much new space there is.
if (newWidth > _cchRowWidth)
{
// Get the attribute that covers the final column of old width.
const auto runPos = FindAttrIndex(_cchRowWidth - 1, nullptr);
auto& run = _list.at(runPos);
// Extend its length by the additional columns we're adding.
run.SetLength(run.GetLength() + newWidth - _cchRowWidth);
// Store that the new total width we represent is the new width.
_cchRowWidth = newWidth;
}
// harder case: new row is shorter.
else
{
// Get the attribute that covers the final column of the new width
size_t CountOfAttr = 0;
const auto runPos = FindAttrIndex(newWidth - 1, &CountOfAttr);
auto& run = _list.at(runPos);
// CountOfAttr was given to us as "how many columns left from this point forward are covered by the returned run"
// So if the original run was B5 covering a 5 size OldWidth and we have a NewWidth of 3
// then when we called FindAttrIndex, it returned the B5 as the pIndexedRun and a 2 for how many more segments it covers
// after and including the 3rd column.
// B5-2 = B3, which is what we desire to cover the new 3 size buffer.
run.SetLength(run.GetLength() - CountOfAttr + 1);
// Store that the new total width we represent is the new width.
_cchRowWidth = newWidth;
// Erase segments after the one we just updated.
_list.erase(_list.cbegin() + runPos + 1, _list.cend());
// NOTE: Under some circumstances here, we have leftover run segments in memory or blank run segments
// in memory. We're not going to waste time redimensioning the array in the heap. We're just noting that the useful
// portions of it have changed.
}
void ATTR_ROW::Reset(const TextAttribute attr)
{
mybase::fill(attr);
}
// Routine Description:
@@ -97,7 +33,7 @@ void ATTR_ROW::Resize(const size_t newWidth)
// - will throw on error
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column) const
{
return GetAttrByColumn(column, nullptr);
return mybase::at(gsl::narrow<UINT>(column));
}
// Routine Description:
@@ -112,73 +48,10 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column) const
TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
size_t* const pApplies) const
{
THROW_HR_IF(E_INVALIDARG, column >= _cchRowWidth);
const auto runPos = FindAttrIndex(column, pApplies);
return _list.at(runPos).GetAttributes();
}
// Routine Description:
// - reports how many runs we have stored (to be used for some optimizations
// Return Value:
// - Count of runs. 1 means we have 1 color to represent the entire row.
size_t ATTR_ROW::GetNumberOfRuns() const noexcept
{
return _list.size();
}
// Routine Description:
// - This routine finds the nth attribute in this ATTR_ROW.
// Arguments:
// - index - which attribute to find
// - applies - on output, contains corrected length of indexed attr.
// for example, if the attribute string was { 5, BLUE } and the requested
// index was 3, CountOfAttr would be 2.
// Return Value:
// - const reference to attribute run object
size_t ATTR_ROW::FindAttrIndex(const size_t index, size_t* const pApplies) const
{
FAIL_FAST_IF(!(index < _cchRowWidth)); // The requested index cannot be longer than the total length described by this set of Attrs.
size_t cTotalLength = 0;
FAIL_FAST_IF(!(_list.size() > 0)); // There should be a non-zero and positive number of items in the array.
// Scan through the internal array from position 0 adding up the lengths that each attribute applies to
auto runPos = _list.cbegin();
do
{
cTotalLength += runPos->GetLength();
if (cTotalLength > index)
{
// If we've just passed up the requested index with the length we added, break early
break;
}
runPos++;
} while (runPos < _list.cend());
// we should have broken before falling out the while case.
// if we didn't break, then this ATTR_ROW wasn't filled with enough attributes for the entire row of characters
FAIL_FAST_IF(runPos >= _list.cend());
// The remaining iterator position is the position of the attribute that is applicable at the position requested (index)
// Calculate its remaining applicability if requested
// The length on which the found attribute applies is the total length seen so far minus the index we were searching for.
FAIL_FAST_IF(!(cTotalLength > index)); // The length of all attributes we counted up so far should be longer than the index requested or we'll underflow.
if (nullptr != pApplies)
{
const auto attrApplies = cTotalLength - index;
FAIL_FAST_IF(!(attrApplies > 0)); // An attribute applies for >0 characters
// MSFT: 17130145 - will restore this and add a better assert to catch the real issue.
//FAIL_FAST_IF(!(attrApplies <= _cchRowWidth)); // An attribute applies for a maximum of the total length available to us
*pApplies = attrApplies;
}
return runPos - _list.cbegin();
UINT applies = 0;
const auto attr = mybase::at(gsl::narrow<UINT>(column), applies);
*pApplies = applies;
return attr;
}
// Routine Description:
@@ -188,11 +61,11 @@ size_t ATTR_ROW::FindAttrIndex(const size_t index, size_t* const pApplies) const
std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
{
std::vector<uint16_t> ids;
for (const auto& run : _list)
for (const auto& run : *this)
{
if (run.GetAttributes().IsHyperlink())
if (run.IsHyperlink())
{
ids.emplace_back(run.GetAttributes().GetHyperlinkId());
ids.emplace_back(run.GetHyperlinkId());
}
}
return ids;
@@ -207,10 +80,8 @@ std::vector<uint16_t> ATTR_ROW::GetHyperlinks()
// - <none>
bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
{
size_t const length = _cchRowWidth - iStart;
const TextAttributeRun run(length, attr);
return SUCCEEDED(InsertAttrRuns({ &run, 1 }, iStart, _cchRowWidth - 1, _cchRowWidth));
mybase::fill(attr, iStart);
return true;
}
// Method Description:
@@ -223,13 +94,7 @@ bool ATTR_ROW::SetAttrToEnd(const UINT iStart, const TextAttribute attr)
// - <none>
void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept
{
for (auto& run : _list)
{
if (run.GetAttributes() == toBeReplacedAttr)
{
run.SetAttributes(replaceWith);
}
}
mybase::replace(toBeReplacedAttr, replaceWith);
}
// Routine Description:
@@ -248,392 +113,12 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
// otherwise STATUS_SUCCESS if we were successful.
[[nodiscard]] HRESULT ATTR_ROW::InsertAttrRuns(const gsl::span<const TextAttributeRun> newAttrs,
const size_t iStart,
const size_t iEnd,
const size_t cBufferWidth)
const size_t /*iEnd*/,
const size_t /*cBufferWidth*/)
try
{
// Definitions:
// Existing Run = The run length encoded color array we're already storing in memory before this was called.
// Insert Run = The run length encoded color array that someone is asking us to inject into our stored memory run.
// New Run = The run length encoded color array that we have to allocate and rebuild to store internally
// which will replace Existing Run at the end of this function.
// Example:
// cBufferWidth = 10.
// Existing Run: R3 -> G5 -> B2
// Insert Run: Y1 -> N1 at iStart = 5 and iEnd = 6
// (rgInsertAttrs is a 2 length array with Y1->N1 in it and cInsertAttrs = 2)
// Final Run: R3 -> G2 -> Y1 -> N1 -> G1 -> B2
// We'll need to know what the last valid column is for some calculations versus iEnd
// because iEnd is specified to us as an inclusive index value.
// Do the -1 math here now so we don't have to have -1s scattered all over this function.
const size_t iLastBufferCol = cBufferWidth - 1;
// If the insertion size is 1, do some pre-processing to
// see if we can get this done quickly.
if (newAttrs.size() == 1)
{
// Get the new color attribute we're trying to apply
const TextAttribute NewAttr = til::at(newAttrs, 0).GetAttributes();
// If the existing run was only 1 element...
// ...and the new color is the same as the old, we don't have to do anything and can exit quick.
if (_list.size() == 1 && _list.at(0).GetAttributes() == NewAttr)
{
return S_OK;
}
// .. otherwise if we internally have a list of 2 or more and we're about to insert a single color
// it's possible that we just walk left-to-right through the row and find a quick exit.
else if (iStart >= 0 && iStart == iEnd)
{
// First we try to find the run where the insertion happens, using lowerBound and upperBound to track
// where we are currently at.
const auto begin = _list.begin();
size_t lowerBound = 0;
size_t upperBound = 0;
for (size_t i = 0; i < _list.size(); i++)
{
const auto curr = begin + i;
upperBound += curr->GetLength();
if (iStart >= lowerBound && iStart < upperBound)
{
// The run that we try to insert into has the same color as the new one.
// e.g.
// AAAAABBBBBBBCCC
// ^
// AAAAABBBBBBBCCC
//
// 'B' is the new color and '^' represents where iStart is. We don't have to
// do anything.
if (curr->GetAttributes() == NewAttr)
{
return S_OK;
}
// If the current run has length of exactly one, we can simply change the attribute
// of the current run.
// e.g.
// AAAAABCCCCCCCCC
// ^
// AAAAADCCCCCCCCC
//
// Here 'D' is the new color.
if (curr->GetLength() == 1)
{
curr->SetAttributes(NewAttr);
return S_OK;
}
// If the insertion happens at current run's lower boundary...
if (iStart == lowerBound && i > 0)
{
const auto prev = std::prev(curr, 1);
// ... and the previous run has the same color as the new one, we can
// just adjust the counts in the existing two elements in our internal list.
// e.g.
// AAAAABBBBBBBCCC
// ^
// AAAAAABBBBBBCCC
//
// Here 'A' is the new color.
if (NewAttr == prev->GetAttributes())
{
prev->IncrementLength();
curr->DecrementLength();
// If we just reduced the right half to zero, just erase it out of the list.
if (curr->GetLength() == 0)
{
_list.erase(curr);
}
return S_OK;
}
}
// If the insertion happens at current run's upper boundary...
if (iStart == upperBound - 1 && i + 1 < _list.size())
{
// ...then let's try our luck with the next run if possible. This is basically the opposite
// of what we did with the previous run.
// e.g.
// AAAAAABBBBBBCCC
// ^
// AAAAABBBBBBBCCC
//
// Here 'B' is the new color.
const auto next = std::next(curr, 1);
if (NewAttr == next->GetAttributes())
{
curr->DecrementLength();
next->IncrementLength();
if (curr->GetLength() == 0)
{
_list.erase(curr);
}
return S_OK;
}
}
}
// Advance one run in the _list.
lowerBound = upperBound;
// The lowerBound is larger than iStart, which means we fail to find an early exit at the run
// where the insertion happens. We can just break out.
if (lowerBound > iStart)
{
break;
}
}
}
}
// If we're about to cover the entire existing run with a new one, we can also make an optimization.
if (iStart == 0 && iEnd == iLastBufferCol)
{
// Just dump what we're given over what we have and call it a day.
_list.assign(newAttrs.begin(), newAttrs.end());
return S_OK;
}
// In the worst case scenario, we will need a new run that is the length of
// The existing run in memory + The new run in memory + 1.
// This worst case occurs when we inject a new item in the middle of an existing run like so
// Existing R3->B5->G2, Insertion Y2 starting at 5 (in the middle of the B5)
// becomes R3->B2->Y2->B1->G2.
// The original run was 3 long. The insertion run was 1 long. We need 1 more for the
// fact that an existing piece of the run was split in half (to hold the latter half).
const size_t cNewRun = _list.size() + newAttrs.size() + 1;
decltype(_list) newRun;
newRun.reserve(cNewRun);
// We will start analyzing from the beginning of our existing run.
// Use some pointers to keep track of where we are in walking through our runs.
// Get the existing run that we'll be updating/manipulating.
const auto existingRun = _list.begin();
auto pExistingRunPos = existingRun;
const auto pExistingRunEnd = _list.end();
auto pInsertRunPos = newAttrs.begin();
size_t cInsertRunRemaining = newAttrs.size();
size_t iExistingRunCoverage = 0;
// Copy the existing run into the new buffer up to the "start index" where the new run will be injected.
// If the new run starts at 0, we have nothing to copy from the beginning.
if (iStart != 0)
{
// While we're less than the desired insertion position...
while (iExistingRunCoverage < iStart)
{
// Add up how much length we can cover by copying an item from the existing run.
iExistingRunCoverage += pExistingRunPos->GetLength();
// Copy it to the new run buffer and advance both pointers.
newRun.push_back(*pExistingRunPos++);
}
// When we get to this point, we've copied full segments from the original existing run
// into our new run buffer. We will have 1 or more full segments of color attributes and
// we MIGHT have to cut the last copied segment's length back depending on where the inserted
// attributes will fall in the final/new run.
// Some examples:
// - Starting with the original string R3 -> G5 -> B2
// - 1. If the insertion is Y5 at start index 3
// We are trying to get a result/final/new run of R3 -> Y5 -> B2.
// We just copied R3 to the new destination buffer and we cang skip down and start inserting the new attrs.
// - 2. If the insertion is Y3 at start index 5
// We are trying to get a result/final/new run of R3 -> G2 -> Y3 -> B2.
// We just copied R3 -> G5 to the new destination buffer with the code above.
// But the insertion is going to cut out some of the length of the G5.
// We need to fix this up below so it says G2 instead to leave room for the Y3 to fit in
// the new/final run.
// Fetch out the length so we can fix it up based on the below conditions.
size_t length = newRun.back().GetLength();
// If we've covered more cells already than the start of the attributes to be inserted...
if (iExistingRunCoverage > iStart)
{
// ..then subtract some of the length of the final cell we copied.
// We want to take remove the difference in distance between the cells we've covered in the new
// run and the insertion point.
// (This turns G5 into G2 from Example 2 just above)
length -= (iExistingRunCoverage - iStart);
}
// Now we're still on that "last cell copied" into the new run.
// If the color of that existing copied cell matches the color of the first segment
// of the run we're about to insert, we can just increment the length to extend the coverage.
if (newRun.back().GetAttributes() == pInsertRunPos->GetAttributes())
{
length += pInsertRunPos->GetLength();
// Since the color matched, we have already "used up" part of the insert run
// and can skip it in our big "memcopy" step below that will copy the bulk of the insert run.
cInsertRunRemaining--;
pInsertRunPos++;
}
// We're done manipulating the length. Store it back.
newRun.back().SetLength(length);
}
// Bulk copy the majority (or all, depending on circumstance) of the insert run into the final run buffer.
std::copy_n(pInsertRunPos, cInsertRunRemaining, std::back_inserter(newRun));
// We're technically done with the insert run now and have 0 remaining, but won't bother updating its pointers
// and counts any further because we won't use them.
// Now we need to move our pointer for the original existing run forward and update our counts
// on how many cells we could have copied from the source before finishing off the new run.
while (iExistingRunCoverage <= iEnd)
{
FAIL_FAST_IF(!(pExistingRunPos != pExistingRunEnd));
iExistingRunCoverage += pExistingRunPos->GetLength();
pExistingRunPos++;
}
// If we still have original existing run cells remaining, copy them into the final new run.
if (pExistingRunPos != pExistingRunEnd || iExistingRunCoverage != (iEnd + 1))
{
// We advanced the existing run pointer and its count to on or past the end of what the insertion run filled in.
// If this ended up being past the end of what the insertion run covers, we have to account for the cells after
// the insertion run but before the next piece of the original existing run.
// The example in this case is if we had...
// Existing Run = R3 -> G5 -> B2 -> X5
// Insert Run = Y2 @ iStart = 7 and iEnd = 8
// ... then at this point in time, our states would look like...
// New Run so far = R3 -> G4 -> Y2
// Existing Run Pointer is at X5
// Existing run coverage count at 3 + 5 + 2 = 10.
// However, in order to get the final desired New Run
// (which is R3 -> G4 -> Y2 -> B1 -> X5)
// we would need to grab a piece of that B2 we already skipped past.
// iExistingRunCoverage = 10. iEnd = 8. iEnd+1 = 9. 10 > 9. So we skipped something.
if (iExistingRunCoverage > (iEnd + 1))
{
// Back up the existing run pointer so we can grab the piece we skipped.
pExistingRunPos--;
// If the color matches what's already in our run, just increment the count value.
// This case is slightly off from the example above. This case is for if the B2 above was actually Y2.
// That Y2 from the existing run is the same color as the Y2 we just filled a few columns left in the final run
// so we can just adjust the final run's column count instead of adding another segment here.
if (newRun.back().GetAttributes() == pExistingRunPos->GetAttributes())
{
size_t length = newRun.back().GetLength();
length += (iExistingRunCoverage - (iEnd + 1));
newRun.back().SetLength(length);
}
else
{
// If the color didn't match, then we just need to copy the piece we skipped and adjust
// its length for the discrepancy in columns not yet covered by the final/new run.
// Move forward to a blank spot in the new run
newRun.emplace_back();
// Copy the existing run's color information to the new run
newRun.back().SetAttributes(pExistingRunPos->GetAttributes());
// Adjust the length of that copied color to cover only the reduced number of columns needed
// now that some have been replaced by the insert run.
newRun.back().SetLength(iExistingRunCoverage - (iEnd + 1));
}
// Now that we're done recovering a piece of the existing run we skipped, move the pointer forward again.
pExistingRunPos++;
}
// OK. In this case, we didn't skip anything. The end of the insert run fell right at a boundary
// in columns that was in the original existing run.
// However, the next piece of the original existing run might happen to have the same color attribute
// as the final piece of what we just copied.
// As an example...
// Existing Run = R3 -> G5 -> B2.
// Insert Run = B5 @ iStart = 3 and iEnd = 7
// New Run so far = R3 -> B5
// New Run desired when done = R3 -> B7
// Existing run pointer is on B2.
// We want to merge the 2 from the B2 into the B5 so we get B7.
else if (newRun.back().GetAttributes() == pExistingRunPos->GetAttributes())
{
// Add the value from the existing run into the current new run position.
size_t length = newRun.back().GetLength();
length += pExistingRunPos->GetLength();
newRun.back().SetLength(length);
// Advance the existing run position since we consumed its value and merged it in.
pExistingRunPos++;
}
// Now bulk copy any segments left in the original existing run
if (pExistingRunPos < pExistingRunEnd)
{
std::copy_n(pExistingRunPos, (pExistingRunEnd - pExistingRunPos), std::back_inserter(newRun));
}
}
// OK, phew. We're done. Now we just need to free the existing run and store the new run in its place.
_list.swap(newRun);
mybase::replace(iStart, mybase::npos, newAttrs.begin(), newAttrs.end());
return S_OK;
}
// Routine Description:
// - packs a vector of TextAttribute into a vector of TextAttributeRun
// Arguments:
// - attrs - text attributes to pack
// Return Value:
// - packed text attribute run
std::vector<TextAttributeRun> ATTR_ROW::PackAttrs(const std::vector<TextAttribute>& attrs)
{
std::vector<TextAttributeRun> runs;
if (attrs.empty())
{
return runs;
}
for (auto attr : attrs)
{
if (runs.empty() || runs.back().GetAttributes() != attr)
{
runs.emplace_back(TextAttributeRun(1, attr));
}
else
{
runs.back().SetLength(runs.back().GetLength() + 1);
}
}
return runs;
}
ATTR_ROW::const_iterator ATTR_ROW::begin() const noexcept
{
return AttrRowIterator(this);
}
ATTR_ROW::const_iterator ATTR_ROW::end() const noexcept
{
return AttrRowIterator::CreateEndIterator(this);
}
ATTR_ROW::const_iterator ATTR_ROW::cbegin() const noexcept
{
return AttrRowIterator(this);
}
ATTR_ROW::const_iterator ATTR_ROW::cend() const noexcept
{
return AttrRowIterator::CreateEndIterator(this);
}
bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept
{
return (a._list.size() == b._list.size() &&
a._list.data() == b._list.data() &&
a._cchRowWidth == b._cchRowWidth);
}
CATCH_RETURN()

View File

@@ -20,37 +20,27 @@ Revision History:
#pragma once
#include "TextAttributeRun.hpp"
#include "AttrRowIterator.hpp"
#include "til/rle.h"
class ATTR_ROW final
#include "TextAttributeRun.hpp"
class ATTR_ROW final : public til::rle<TextAttribute, unsigned int>
{
public:
using const_iterator = typename AttrRowIterator;
using mybase = til::rle<TextAttribute, unsigned int>;
ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
noexcept;
using const_iterator = mybase::const_iterator;
using const_reverse_iterator = mybase::const_reverse_iterator;
~ATTR_ROW() = default;
ATTR_ROW(const ATTR_ROW&) = default;
ATTR_ROW& operator=(const ATTR_ROW&) = default;
ATTR_ROW(ATTR_ROW&&)
noexcept = default;
ATTR_ROW& operator=(ATTR_ROW&&) noexcept = default;
using mybase::mybase; // use base constructor
TextAttribute GetAttrByColumn(const size_t column) const;
TextAttribute GetAttrByColumn(const size_t column,
size_t* const pApplies) const;
size_t GetNumberOfRuns() const noexcept;
size_t FindAttrIndex(const size_t index,
size_t* const pApplies) const;
std::vector<uint16_t> GetHyperlinks();
bool SetAttrToEnd(const UINT iStart, const TextAttribute attr);
bool SetAttrToEnd(const unsigned int iStart, const TextAttribute attr);
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept;
void Resize(const size_t newWidth);
@@ -60,26 +50,19 @@ public:
const size_t iEnd,
const size_t cBufferWidth);
static std::vector<TextAttributeRun> PackAttrs(const std::vector<TextAttribute>& attrs);
using mybase::begin;
using mybase::cbegin;
using mybase::cend;
using mybase::end;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
using mybase::operator==;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
friend bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept;
friend class AttrRowIterator;
friend class ROW;
private:
void Reset(const TextAttribute attr);
boost::container::small_vector<TextAttributeRun, 1> _list;
size_t _cchRowWidth;
#ifdef UNIT_TESTING
friend class AttrRowTests;
friend class CommonState;
#endif
};

View File

@@ -22,29 +22,23 @@ Revision History:
#include "TextAttribute.hpp"
class TextAttributeRun final
class TextAttributeRun final : public std::pair<TextAttribute, unsigned int>
{
public:
TextAttributeRun() = default;
TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept :
_cchLength(gsl::narrow<unsigned int>(cchLength))
using mybase = std::pair<TextAttribute, unsigned int>;
using mybase::mybase;
TextAttributeRun(const size_t cchLength, const TextAttribute attr) :
mybase(attr, gsl::narrow<unsigned int>(cchLength))
{
SetAttributes(attr);
}
size_t GetLength() const noexcept { return _cchLength; }
void SetLength(const size_t cchLength) noexcept { _cchLength = gsl::narrow<unsigned int>(cchLength); }
void IncrementLength() noexcept { _cchLength++; }
void DecrementLength() noexcept { _cchLength--; }
size_t GetLength() const noexcept { return mybase::second; }
void SetLength(const size_t cchLength) noexcept { mybase::second = gsl::narrow<unsigned int>(cchLength); }
void IncrementLength() noexcept { mybase::second++; }
void DecrementLength() noexcept { mybase::second--; }
const TextAttribute& GetAttributes() const noexcept { return _attributes; }
void SetAttributes(const TextAttribute textAttribute) noexcept { _attributes = textAttribute; }
private:
unsigned int _cchLength{ 0 };
TextAttribute _attributes{ 0 };
#ifdef UNIT_TESTING
friend class AttrRowTests;
#endif
const TextAttribute& GetAttributes() const noexcept { return mybase::first; }
void SetAttributes(const TextAttribute textAttribute) noexcept { mybase::first = textAttribute; }
};

View File

@@ -11,7 +11,6 @@
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<ClCompile Include="..\AttrRow.cpp" />
<ClCompile Include="..\AttrRowIterator.cpp" />
<ClCompile Include="..\cursor.cpp" />
<ClCompile Include="..\OutputCell.cpp" />
<ClCompile Include="..\OutputCellIterator.cpp" />
@@ -34,7 +33,6 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\AttrRow.hpp" />
<ClInclude Include="..\AttrRowIterator.hpp" />
<ClInclude Include="..\cursor.h" />
<ClInclude Include="..\DbcsAttribute.hpp" />
<ClInclude Include="..\ICharRow.hpp" />

View File

@@ -30,7 +30,6 @@ PRECOMPILED_INCLUDE = ..\precomp.h
SOURCES= \
..\AttrRow.cpp \
..\AttrRowIterator.cpp \
..\cursor.cpp \
..\OutputCell.cpp \
..\OutputCellIterator.cpp \

View File

@@ -15,8 +15,8 @@ Author(s):
#pragma once
#include "AttrRowIterator.hpp"
#include "CharRow.hpp"
#include "AttrRow.hpp"
#include "OutputCellView.hpp"
#include "../../types/inc/viewport.hpp"
@@ -55,7 +55,7 @@ protected:
OutputCellView _view;
const ROW* _pRow;
AttrRowIterator _attrIter;
ATTR_ROW::const_iterator _attrIter;
const TextBuffer& _buffer;
const Microsoft::Console::Types::Viewport _bounds;
bool _exceeded;

View File

@@ -1,731 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
#include "../../../inc/consoletaeftemplates.hpp"
#include "../textBuffer.hpp"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
namespace WEX
{
namespace TestExecution
{
template<>
class VerifyOutputTraits<TextAttributeRun>
{
public:
static WEX::Common::NoThrowString ToString(const TextAttributeRun& tar)
{
return WEX::Common::NoThrowString().Format(
L"Length:%d, attr:%s",
tar.GetLength(),
VerifyOutputTraits<TextAttribute>::ToString(tar.GetAttributes()).GetBuffer());
}
};
template<>
class VerifyCompareTraits<TextAttributeRun, TextAttributeRun>
{
public:
static bool AreEqual(const TextAttributeRun& expected, const TextAttributeRun& actual)
{
return expected.GetAttributes() == actual.GetAttributes() &&
expected.GetLength() == actual.GetLength();
}
static bool AreSame(const TextAttributeRun& expected, const TextAttributeRun& actual)
{
return &expected == &actual;
}
static bool IsLessThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
static bool IsGreaterThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
static bool IsNull(const TextAttributeRun& object)
{
return object.GetAttributes().IsLegacy() && object.GetAttributes().GetLegacyAttributes() == 0 &&
object.GetLength() == 0;
}
};
}
}
class AttrRowTests
{
ATTR_ROW* pSingle;
ATTR_ROW* pChain;
short _sDefaultLength = 80;
short _sDefaultChainLength = 6;
short sChainSegLength;
short sChainLeftover;
short sChainSegmentsNeeded;
WORD __wDefaultAttr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
WORD __wDefaultChainAttr = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
TextAttribute _DefaultAttr = TextAttribute(__wDefaultAttr);
TextAttribute _DefaultChainAttr = TextAttribute(__wDefaultChainAttr);
TEST_CLASS(AttrRowTests);
TEST_METHOD_SETUP(MethodSetup)
{
pSingle = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
// Segment length is the expected length divided by the row length
// E.g. row of 80, 4 segments, 20 segment length each
sChainSegLength = _sDefaultLength / _sDefaultChainLength;
// Leftover is spaces that don't fit evenly
// E.g. row of 81, 4 segments, 1 leftover length segment
sChainLeftover = _sDefaultLength % _sDefaultChainLength;
// Start with the number of segments we expect
sChainSegmentsNeeded = _sDefaultChainLength;
// If we had a remainder, add one more segment
if (sChainLeftover)
{
sChainSegmentsNeeded++;
}
// Create the chain
pChain = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
pChain->_list.resize(sChainSegmentsNeeded);
// Attach all chain segments that are even multiples of the row length
for (short iChain = 0; iChain < _sDefaultChainLength; iChain++)
{
TextAttributeRun* pRun = &pChain->_list[iChain];
pRun->SetAttributes(TextAttribute{ gsl::narrow_cast<WORD>(iChain) }); // Just use the chain position as the value
pRun->SetLength(sChainSegLength);
}
if (sChainLeftover > 0)
{
// If we had a leftover, then this chain is one longer than we expected (the default length)
// So use it as the index (because indices start at 0)
TextAttributeRun* pRun = &pChain->_list[_sDefaultChainLength];
pRun->SetAttributes(_DefaultChainAttr);
pRun->SetLength(sChainLeftover);
}
return true;
}
TEST_METHOD_CLEANUP(MethodCleanup)
{
delete pSingle;
delete pChain;
return true;
}
TEST_METHOD(TestInitialize)
{
// Properties needed for test
const WORD wAttr = FOREGROUND_RED | BACKGROUND_BLUE;
TextAttribute attr = TextAttribute(wAttr);
// Cases to test
ATTR_ROW* pTestItems[]{ pSingle, pChain };
// Loop cases
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
{
ATTR_ROW* pUnderTest = pTestItems[iIndex];
pUnderTest->Reset(attr);
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), attr);
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
}
}
// Routine Description:
// - Packs an array of words representing attributes into the more compact storage form used by the row.
// Arguments:
// - rgAttrs - Array of words representing the attribute associated with each character position in the row.
// - cRowLength - Length of preceding array.
// - outAttrRun - reference to unique_ptr that will contain packed attr run on success.
// Return Value:
// - Success if success. Buffer too small if row length is incorrect.
HRESULT PackAttrs(_In_reads_(cRowLength) const TextAttribute* const rgAttrs,
const size_t cRowLength,
_Inout_ std::unique_ptr<TextAttributeRun[]>& outAttrRun,
_Out_ size_t* const cOutAttrRun)
{
RETURN_HR_IF(E_NOT_SUFFICIENT_BUFFER, cRowLength == 0);
// first count up the deltas in the array
size_t cDeltas = 1;
const TextAttribute* pPrevAttr = &rgAttrs[0];
for (size_t i = 1; i < cRowLength; i++)
{
const TextAttribute* pCurAttr = &rgAttrs[i];
if (*pCurAttr != *pPrevAttr)
{
cDeltas++;
}
pPrevAttr = pCurAttr;
}
// This whole situation was too complicated with a one off holder for one row run
// new method:
// delete the old buffer
// make a new buffer, one run + one run for each change
// set the values for each run one run index at a time
std::unique_ptr<TextAttributeRun[]> attrRun = std::make_unique<TextAttributeRun[]>(cDeltas);
RETURN_HR_IF_NULL(E_OUTOFMEMORY, attrRun);
TextAttributeRun* pCurrentRun = attrRun.get();
pCurrentRun->SetAttributes(rgAttrs[0]);
pCurrentRun->SetLength(1);
for (size_t i = 1; i < cRowLength; i++)
{
if (pCurrentRun->GetAttributes() == rgAttrs[i])
{
pCurrentRun->SetLength(pCurrentRun->GetLength() + 1);
}
else
{
pCurrentRun++;
pCurrentRun->SetAttributes(rgAttrs[i]);
pCurrentRun->SetLength(1);
}
}
attrRun.swap(outAttrRun);
*cOutAttrRun = cDeltas;
return S_OK;
}
NoThrowString LogRunElement(_In_ TextAttributeRun& run)
{
return NoThrowString().Format(L"%wc%d", run.GetAttributes().GetLegacyAttributes(), run.GetLength());
}
void LogChain(_In_ PCWSTR pwszPrefix,
boost::container::small_vector_base<TextAttributeRun>& chain)
{
NoThrowString str(pwszPrefix);
if (chain.size() > 0)
{
str.Append(LogRunElement(chain[0]));
for (size_t i = 1; i < chain.size(); i++)
{
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
}
}
Log::Comment(str);
}
void LogChain(_In_ PCWSTR pwszPrefix,
std::vector<TextAttributeRun>& chain)
{
NoThrowString str(pwszPrefix);
if (chain.size() > 0)
{
str.Append(LogRunElement(chain[0]));
for (size_t i = 1; i < chain.size(); i++)
{
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
}
}
Log::Comment(str);
}
void DoTestInsertAttrRuns(UINT& uiStartPos, WORD& ch1, UINT& uiChar1Length, WORD& ch2, UINT& uiChar2Length)
{
Log::Comment(String().Format(L"StartPos: %d, Char1: %wc, Char1Length: %d, Char2: %wc, Char2Length: %d",
uiStartPos,
ch1,
uiChar1Length,
ch2,
uiChar2Length));
bool const fUseStr2 = (ch2 != L'0');
// Set up our "original row" that we are going to try to insert into.
// This will represent a 10 column run of R3->B5->G2 that we will use for all tests.
ATTR_ROW originalRow{ static_cast<UINT>(_sDefaultLength), _DefaultAttr };
originalRow._list.resize(3);
originalRow._cchRowWidth = 10;
originalRow._list[0].SetAttributes(TextAttribute{ 'R' });
originalRow._list[0].SetLength(3);
originalRow._list[1].SetAttributes(TextAttribute{ 'B' });
originalRow._list[1].SetLength(5);
originalRow._list[2].SetAttributes(TextAttribute{ 'G' });
originalRow._list[2].SetLength(2);
LogChain(L"Original: ", originalRow._list);
// Set up our "insertion run"
size_t cInsertRow = 1;
if (fUseStr2)
{
cInsertRow++;
}
std::vector<TextAttributeRun> insertRow;
insertRow.resize(cInsertRow);
insertRow[0].SetAttributes(TextAttribute{ ch1 });
insertRow[0].SetLength(uiChar1Length);
if (fUseStr2)
{
insertRow[1].SetAttributes(TextAttribute{ ch2 });
insertRow[1].SetLength(uiChar2Length);
}
LogChain(L"Insert: ", insertRow);
Log::Comment(NoThrowString().Format(L"At Index: %d", uiStartPos));
UINT uiTotalLength = uiChar1Length;
if (fUseStr2)
{
uiTotalLength += uiChar2Length;
}
VERIFY_IS_TRUE((uiStartPos + uiTotalLength) >= 1); // assert we won't underflow.
UINT const uiEndPos = uiStartPos + uiTotalLength - 1;
// Calculate our expected final/result run by unpacking original, laying our insertion on it at the index
// then using our pack function to repack it.
// This method is easy to understand and very reliable, but its performance is bad.
// The InsertAttrRuns method we test against below is hard to understand but very high performance in production.
// - 1. Unpack
std::vector<TextAttribute> unpackedOriginal = { originalRow.begin(), originalRow.end() };
// - 2. Overlay insertion
UINT uiInsertedCount = 0;
UINT uiInsertIndex = 0;
// --- Walk through the unpacked run from start to end....
for (UINT uiUnpackedIndex = uiStartPos; uiUnpackedIndex <= uiEndPos; uiUnpackedIndex++)
{
// Pull the item from the insert run to analyze.
TextAttributeRun run = insertRow[uiInsertIndex];
// Copy the attribute from the run into the unpacked array
unpackedOriginal[uiUnpackedIndex] = run.GetAttributes();
// Increment how many times we've copied this particular portion of the run
uiInsertedCount++;
// If we've now inserted enough of them to match the length, advance the insert index and reset the counter.
if (uiInsertedCount >= run.GetLength())
{
uiInsertIndex++;
uiInsertedCount = 0;
}
}
// - 3. Pack.
std::unique_ptr<TextAttributeRun[]> packedRun;
size_t cPackedRun = 0;
VERIFY_SUCCEEDED(PackAttrs(unpackedOriginal.data(), originalRow._cchRowWidth, packedRun, &cPackedRun));
// Now send parameters into InsertAttrRuns and get its opinion on the subject.
VERIFY_SUCCEEDED(originalRow.InsertAttrRuns({ insertRow.data(), insertRow.size() }, uiStartPos, uiEndPos, (UINT)originalRow._cchRowWidth));
// Compare and ensure that the expected and actual match.
VERIFY_ARE_EQUAL(cPackedRun, originalRow._list.size(), L"Ensure that number of array elements required for RLE are the same.");
std::vector<TextAttributeRun> packedRunExpected;
std::copy_n(packedRun.get(), cPackedRun, std::back_inserter(packedRunExpected));
LogChain(L"Expected: ", packedRunExpected);
LogChain(L"Actual: ", originalRow._list);
for (size_t testIndex = 0; testIndex < cPackedRun; testIndex++)
{
VERIFY_ARE_EQUAL(packedRun[testIndex], originalRow._list[testIndex]);
}
}
TEST_METHOD(TestInsertAttrRunsSingle)
{
UINT const uiTestRunLength = 10;
UINT uiStartPos = 0;
WORD ch1 = L'0';
UINT uiChar1Length = 0;
WORD ch2 = L'0';
UINT uiChar2Length = 0;
Log::Comment(L"Test inserting a single item of a variable length into the run.");
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
{
ch1 = rgch1Options[iCh1Option];
for (UINT iCh1Length = 1; iCh1Length <= uiTestRunLength; iCh1Length++)
{
uiChar1Length = iCh1Length;
// We can't try to insert a run that's longer than would fit.
// If the run is of length 10 and we're trying to insert a length of 10,
// we can only insert at position 0.
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
// And so on...
UINT const uiMaxPos = uiTestRunLength - uiChar1Length;
for (UINT iStartPos = 0; iStartPos < uiMaxPos; iStartPos++)
{
uiStartPos = iStartPos;
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
}
}
}
}
TEST_METHOD(TestInsertAttrRunsMultiple)
{
UINT const uiTestRunLength = 10;
UINT uiStartPos = 0;
WORD ch1 = L'0';
UINT uiChar1Length = 0;
WORD ch2 = L'0';
UINT uiChar2Length = 0;
Log::Comment(L"Test inserting a multiple item run with each piece having variable length into the existing run.");
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
{
ch1 = rgch1Options[iCh1Option];
UINT const uiMaxCh1Length = uiTestRunLength - 1; // leave at least 1 space for the second piece of the insert run.
for (UINT iCh1Length = 1; iCh1Length <= uiMaxCh1Length; iCh1Length++)
{
uiChar1Length = iCh1Length;
WORD rgch2Options[] = { L'Y' };
for (size_t iCh2Option = 0; iCh2Option < ARRAYSIZE(rgch2Options); iCh2Option++)
{
ch2 = rgch2Options[iCh2Option];
// When choosing the length of the second item, it can't be bigger than the remaining space in the run
// when accounting for the length of the first piece chosen.
// For example if the total run length is 10 and the first piece chosen was 8 long,
// the second piece can only be 1 or 2 long.
UINT const uiMaxCh2Length = uiTestRunLength - uiMaxCh1Length;
for (UINT iCh2Length = 1; iCh2Length <= uiMaxCh2Length; iCh2Length++)
{
uiChar2Length = iCh2Length;
// We can't try to insert a run that's longer than would fit.
// If the run is of length 10 and we're trying to insert a total length of 10,
// we can only insert at position 0.
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
// And so on...
UINT const uiMaxPos = uiTestRunLength - (uiChar1Length + uiChar2Length);
for (UINT iStartPos = 0; iStartPos <= uiMaxPos; iStartPos++)
{
uiStartPos = iStartPos;
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
}
}
}
}
}
}
TEST_METHOD(TestUnpackAttrs)
{
Log::Comment(L"Checking unpack of a single color for the entire length");
{
const std::vector<TextAttribute> attrs{ pSingle->begin(), pSingle->end() };
for (auto& attr : attrs)
{
VERIFY_ARE_EQUAL(attr, _DefaultAttr);
}
}
Log::Comment(L"Checking unpack of the multiple color chain");
const std::vector<TextAttribute> attrs{ pChain->begin(), pChain->end() };
short cChainRun = 0; // how long we've been looking at the current piece of the chain
short iChainSegIndex = 0; // which piece of the chain we should be on right now
for (auto& attr : attrs)
{
// by default the chain was assembled above to have the chain segment index be the attribute
TextAttribute MatchingAttr = TextAttribute(iChainSegIndex);
// However, if the index is greater than the expected chain length, a remainder piece was made with a default attribute
if (iChainSegIndex >= _sDefaultChainLength)
{
MatchingAttr = _DefaultChainAttr;
}
VERIFY_ARE_EQUAL(attr, MatchingAttr);
// Add to the chain run
cChainRun++;
// If the chain run is greater than the length the segments were specified to be
if (cChainRun >= sChainSegLength)
{
// reset to 0
cChainRun = 0;
// move to the next chain segment down the line
iChainSegIndex++;
}
}
}
TEST_METHOD(TestReverseIteratorWalkFromMiddle)
{
// GH #3409, walking backwards through color range runs out of bounds
// We're going to create an attribute row with assorted colors and varying lengths
// just like the row of text on the Ubuntu prompt line that triggered this bug being found.
// Then we're going to walk backwards through the iterator like a selection-expand-to-left
// operation and ensure we don't run off the bounds.
// walk the chain, from index, stepSize at a time
// ensure we don't crash
auto testWalk = [](ATTR_ROW* chain, size_t index, int stepSize) {
// move to starting index
auto iter = chain->cbegin();
iter += index;
// Now walk backwards in a loop until 0.
while (iter)
{
iter -= stepSize;
}
Log::Comment(L"We made it through without crashing!");
};
// take one step of size stepSize on the chain
// index is where we start from
// expectedAttribute is what we expect to read here
auto verifyStep = [](ATTR_ROW* chain, size_t index, int stepSize, TextAttribute expectedAttribute) {
// move to starting index
auto iter = chain->cbegin();
iter += index;
// Now step backwards
iter -= stepSize;
VERIFY_ARE_EQUAL(expectedAttribute, *iter);
};
Log::Comment(L"Reverse iterate through ubuntu prompt");
{
// Create attr row representing a buffer that's 121 wide.
auto chain = std::make_unique<ATTR_ROW>(121, _DefaultAttr);
// The repro case had 4 chain segments.
chain->_list.resize(4);
// The color 10 went for the first 18.
chain->_list[0].SetAttributes(TextAttribute(0xA));
chain->_list[0].SetLength(18);
// Default color for the next 1
chain->_list[1].SetAttributes(TextAttribute());
chain->_list[1].SetLength(1);
// Color 12 for the next 29
chain->_list[2].SetAttributes(TextAttribute(0xC));
chain->_list[2].SetLength(29);
// Then default color to end the run
chain->_list[3].SetAttributes(TextAttribute());
chain->_list[3].SetLength(73);
// The sum of the lengths should be 121.
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength + chain->_list[3]._cchLength);
auto index = chain->_list[0].GetLength();
auto stepSize = 1;
testWalk(chain.get(), index, stepSize);
}
Log::Comment(L"Reverse iterate across a text run in the chain");
{
// Create attr row representing a buffer that's 3 wide.
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
// The repro case had 3 chain segments.
chain->_list.resize(3);
// The color 10 went for the first 1.
chain->_list[0].SetAttributes(TextAttribute(0xA));
chain->_list[0].SetLength(1);
// The color 11 for the next 1
chain->_list[1].SetAttributes(TextAttribute(0xB));
chain->_list[1].SetLength(1);
// Color 12 for the next 1
chain->_list[2].SetAttributes(TextAttribute(0xC));
chain->_list[2].SetLength(1);
// The sum of the lengths should be 3.
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
// on 'ABC', step from B to A
auto index = 1;
auto stepSize = 1;
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
}
Log::Comment(L"Reverse iterate across two text runs in the chain");
{
// Create attr row representing a buffer that's 3 wide.
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
// The repro case had 3 chain segments.
chain->_list.resize(3);
// The color 10 went for the first 1.
chain->_list[0].SetAttributes(TextAttribute(0xA));
chain->_list[0].SetLength(1);
// The color 11 for the next 1
chain->_list[1].SetAttributes(TextAttribute(0xB));
chain->_list[1].SetLength(1);
// Color 12 for the next 1
chain->_list[2].SetAttributes(TextAttribute(0xC));
chain->_list[2].SetLength(1);
// The sum of the lengths should be 3.
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
// on 'ABC', step from C to A
auto index = 2;
auto stepSize = 2;
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
}
}
TEST_METHOD(TestSetAttrToEnd)
{
const WORD wTestAttr = FOREGROUND_BLUE | BACKGROUND_GREEN;
TextAttribute TestAttr = TextAttribute(wTestAttr);
Log::Comment(L"FIRST: Set index to > 0 to test making/modifying chains");
const short iTestIndex = 50;
VERIFY_IS_TRUE(iTestIndex >= 0 && iTestIndex < _sDefaultLength);
Log::Comment(L"SetAttrToEnd for single color applied to whole string.");
pSingle->SetAttrToEnd(iTestIndex, TestAttr);
// Was 1 (single), should now have 2 segments
VERIFY_ARE_EQUAL(pSingle->_list.size(), 2u);
VERIFY_ARE_EQUAL(pSingle->_list[0].GetAttributes(), _DefaultAttr);
VERIFY_ARE_EQUAL(pSingle->_list[0].GetLength(), (unsigned int)(_sDefaultLength - (_sDefaultLength - iTestIndex)));
VERIFY_ARE_EQUAL(pSingle->_list[1].GetAttributes(), TestAttr);
VERIFY_ARE_EQUAL(pSingle->_list[1].GetLength(), (unsigned int)(_sDefaultLength - iTestIndex));
Log::Comment(L"SetAttrToEnd for existing chain of multiple colors.");
pChain->SetAttrToEnd(iTestIndex, TestAttr);
// From 7 segments down to 5.
VERIFY_ARE_EQUAL(pChain->_list.size(), 5u);
// Verify chain colors and lengths
VERIFY_ARE_EQUAL(TextAttribute(0), pChain->_list[0].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[0].GetLength(), (unsigned int)13);
VERIFY_ARE_EQUAL(TextAttribute(1), pChain->_list[1].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[1].GetLength(), (unsigned int)13);
VERIFY_ARE_EQUAL(TextAttribute(2), pChain->_list[2].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[2].GetLength(), (unsigned int)13);
VERIFY_ARE_EQUAL(TextAttribute(3), pChain->_list[3].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[3].GetLength(), (unsigned int)11);
VERIFY_ARE_EQUAL(TestAttr, pChain->_list[4].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[4].GetLength(), (unsigned int)30);
Log::Comment(L"SECOND: Set index to 0 to test replacing anything with a single");
ATTR_ROW* pTestItems[]{ pSingle, pChain };
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
{
ATTR_ROW* pUnderTest = pTestItems[iIndex];
pUnderTest->SetAttrToEnd(0, TestAttr);
// should be down to 1 attribute set from beginning to end of string
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
// singular pair should contain the color
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), TestAttr);
// and its length should be the length of the whole string
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
}
}
TEST_METHOD(TestTotalLength)
{
ATTR_ROW* pTestItems[]{ pSingle, pChain };
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
{
ATTR_ROW* pUnderTest = pTestItems[iIndex];
const size_t Result = pUnderTest->_cchRowWidth;
VERIFY_ARE_EQUAL((short)Result, _sDefaultLength);
}
}
TEST_METHOD(TestResize)
{
pSingle->Resize(240);
pChain->Resize(240);
pSingle->Resize(255);
pChain->Resize(255);
pSingle->Resize(255);
pChain->Resize(255);
pSingle->Resize(60);
pChain->Resize(60);
pSingle->Resize(60);
pChain->Resize(60);
VERIFY_THROWS_SPECIFIC(pSingle->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
VERIFY_THROWS_SPECIFIC(pChain->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
}
};

View File

@@ -6,11 +6,10 @@
<RootNamespace>TextBufferUnitTests</RootNamespace>
<ProjectName>TextBuffer.Unit.Tests</ProjectName>
<TargetName>TextBuffer.Unit.Tests</TargetName>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<ClCompile Include="AttrRowTests.cpp" />
<ClCompile Include="ReflowTests.cpp" />
<ClCompile Include="TextColorTests.cpp" />
<ClCompile Include="TextAttributeTests.cpp" />

View File

@@ -14,7 +14,6 @@ DLLDEF =
SOURCES = \
$(SOURCES) \
AttrRowTests.cpp \
ReflowTests.cpp \
TextColorTests.cpp \
TextAttributeTests.cpp \

View File

@@ -782,8 +782,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Switch to the next MRU tab, which is the fourth tab");
TestOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleNextTab(nullptr, eventArgs);
page->_SelectNextTab(true, nullptr);
});
Log::Comment(L"Sleep to let events propagate");
@@ -804,8 +803,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Switch to the next MRU tab, which is the second tab");
TestOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleNextTab(nullptr, eventArgs);
page->_SelectNextTab(true, nullptr);
});
Log::Comment(L"Sleep to let events propagate");
@@ -829,8 +827,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Switch to the next in-order tab, which is the third tab");
TestOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleNextTab(nullptr, eventArgs);
page->_SelectNextTab(true, nullptr);
});
TestOnUIThread([&page]() {
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
@@ -842,8 +839,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Switch to the next in-order tab, which is the fourth tab");
TestOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleNextTab(nullptr, eventArgs);
page->_SelectNextTab(true, nullptr);
});
TestOnUIThread([&page]() {
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
@@ -916,7 +912,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Switch to the next MRU tab, which is the third tab");
RunOnUIThread([&page]() {
page->_SelectNextTab(true);
page->_SelectNextTab(true, nullptr);
// In the course of a single tick, the Command Palette will:
// * open
// * select the proper tab from the mru's list

View File

@@ -56,7 +56,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
// Append a "\." to the given path, so that this will work in "C:\"
std::wstring cmdline = fmt::format(L"\"{}\" -d \"{}\\.\"", GetWtExePath(), pszName.get());
auto cmdline{ wil::str_printf<std::wstring>(LR"-("%s" -d "%s\.")-", GetWtExePath().c_str(), pszName.get()) };
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
nullptr,
cmdline.data(),

View File

@@ -89,15 +89,23 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleNextTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_SelectNextTab(true);
args.Handled(true);
const auto& realArgs = args.ActionArgs().try_as<NextTabArgs>();
if (realArgs)
{
_SelectNextTab(true, realArgs.SwitcherMode());
args.Handled(true);
}
}
void TerminalPage::_HandlePrevTab(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_SelectNextTab(false);
args.Handled(true);
const auto& realArgs = args.ActionArgs().try_as<PrevTabArgs>();
if (realArgs)
{
_SelectNextTab(false, realArgs.SwitcherMode());
args.Handled(true);
}
}
void TerminalPage::_HandleSendInput(const IInspectable& /*sender*/,

View File

@@ -530,6 +530,15 @@ namespace winrt::TerminalApp::implementation
// which will cause us to refresh the list of filterable commands.
_searchBox().Text(L"");
_searchBox().Focus(FocusState::Programmatic);
if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(_searchBox()) })
{
automationPeer.RaiseNotificationEvent(
Automation::Peers::AutomationNotificationKind::ActionCompleted,
Automation::Peers::AutomationNotificationProcessing::CurrentThenMostRecent,
fmt::format(std::wstring_view{ RS_(L"CommandPalette_NestedCommandAnnouncement") }, ParentCommandName()),
L"CommandPaletteNestingLevelChanged" /* unique name for this notification category */);
}
}
// Method Description:
@@ -768,7 +777,19 @@ namespace winrt::TerminalApp::implementation
if (_currentMode == CommandPaletteMode::TabSearchMode || _currentMode == CommandPaletteMode::ActionMode)
{
_noMatchesText().Visibility(_filteredActions.Size() > 0 ? Visibility::Collapsed : Visibility::Visible);
const auto currentNeedleHasResults{ _filteredActions.Size() > 0 };
_noMatchesText().Visibility(currentNeedleHasResults ? Visibility::Collapsed : Visibility::Visible);
if (!currentNeedleHasResults)
{
if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(_searchBox()) })
{
automationPeer.RaiseNotificationEvent(
Automation::Peers::AutomationNotificationKind::ActionCompleted,
Automation::Peers::AutomationNotificationProcessing::ImportantMostRecent,
NoMatchesText(), // NoMatchesText contains the right text for the current mode
L"CommandPaletteResultAnnouncement" /* unique name for this notification */);
}
}
}
else
{
@@ -897,6 +918,9 @@ namespace winrt::TerminalApp::implementation
{
_currentMode = mode;
const auto currentlyVisible{ Visibility() == Visibility::Visible };
auto modeAnnouncementResourceKey{ USES_RESOURCE(L"CommandPaletteModeAnnouncement_ActionMode") };
ParsedCommandLineText(L"");
_searchBox().Text(L"");
_searchBox().Select(_searchBox().Text().size(), 0);
@@ -912,6 +936,7 @@ namespace winrt::TerminalApp::implementation
NoMatchesText(RS_(L"TabSwitcher_NoMatchesText"));
ControlName(RS_(L"TabSwitcherControlName"));
PrefixCharacter(L"");
modeAnnouncementResourceKey = USES_RESOURCE(L"CommandPaletteModeAnnouncement_TabSearchSwitchMode");
break;
}
case CommandPaletteMode::CommandlineMode:
@@ -919,6 +944,7 @@ namespace winrt::TerminalApp::implementation
NoMatchesText(L"");
ControlName(RS_(L"CommandPaletteControlName"));
PrefixCharacter(L"");
modeAnnouncementResourceKey = USES_RESOURCE(L"CommandPaletteModeAnnouncement_CommandlineMode");
break;
case CommandPaletteMode::ActionMode:
default:
@@ -926,9 +952,23 @@ namespace winrt::TerminalApp::implementation
NoMatchesText(RS_(L"CommandPalette_NoMatchesText/Text"));
ControlName(RS_(L"CommandPaletteControlName"));
PrefixCharacter(L">");
// modeAnnouncementResourceKey is already set to _ActionMode
// We did this above to deduce the type (and make it easier on ourselves later).
break;
}
if (currentlyVisible)
{
if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(_searchBox()) })
{
automationPeer.RaiseNotificationEvent(
Automation::Peers::AutomationNotificationKind::ActionCompleted,
Automation::Peers::AutomationNotificationProcessing::CurrentThenMostRecent,
GetLibraryResourceString(modeAnnouncementResourceKey),
L"CommandPaletteModeSwitch" /* unique ID for this notification */);
}
}
// The smooth remove/add animations that happen during
// UpdateFilteredActions don't work very well when switching between
// modes because of the sheer amount of remove/adds. So, let's just

View File

@@ -418,10 +418,10 @@
<value>Do you want to close all tabs?</value>
</data>
<data name="CloseReadOnlyDialog.CloseButtonText" xml:space="preserve">
<value>Close anyway</value>
<value>Cancel</value>
</data>
<data name="CloseReadOnlyDialog.PrimaryButtonText" xml:space="preserve">
<value>Cancel</value>
<value>Close anyway</value>
</data>
<data name="CloseReadOnlyDialog.Title" xml:space="preserve">
<value>Warning</value>
@@ -459,6 +459,22 @@
<data name="CommandPalette_NoMatchesText.Text" xml:space="preserve">
<value>No matching commands</value>
</data>
<data name="CommandPaletteModeAnnouncement_ActionMode" xml:space="preserve">
<value>Action search mode</value>
<comment>This text will be read aloud using assistive technologies when the command palette switches into action (command search) mode.</comment>
</data>
<data name="CommandPaletteModeAnnouncement_TabSearchSwitchMode" xml:space="preserve">
<value>Tab title mode</value>
<comment>This text will be read aloud using assistive technologies when the command palette switches into a mode that filters tab names.</comment>
</data>
<data name="CommandPaletteModeAnnouncement_CommandlineMode" xml:space="preserve">
<value>Command-line mode</value>
<comment>This text will be read aloud using assistive technologies when the command palette switches into the raw commandline parsing mode.</comment>
</data>
<data name="CommandPalette_NestedCommandAnnouncement" xml:space="preserve">
<value>More options for "{}"</value>
<comment>This text will be read aloud using assistive technologies when the user selects a command that has additional options. The {} will be expanded to the name of the command containing more options.</comment>
</data>
<data name="CommandPalette_ParsedCommandLine" xml:space="preserve">
<value>Executing command line will invoke the following commands:</value>
<comment>Will be followed by a list of strings describing parsed commands</comment>

View File

@@ -1339,8 +1339,8 @@ namespace winrt::TerminalApp::implementation
{
ContentDialogResult warningResult = co_await _ShowCloseReadOnlyDialog();
// The primary action is canceling the removal
if (warningResult == ContentDialogResult::Primary)
// If the user didn't explicitly click on close tab - leave
if (warningResult != ContentDialogResult::Primary)
{
co_return;
}
@@ -1527,10 +1527,10 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Sets focus to the tab to the right or left the currently selected tab.
void TerminalPage::_SelectNextTab(const bool bMoveRight)
void TerminalPage::_SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference<Microsoft::Terminal::Settings::Model::TabSwitcherMode>& customTabSwitcherMode)
{
const auto index{ _GetFocusedTabIndex().value_or(0) };
const auto tabSwitchMode = _settings.GlobalSettings().TabSwitcherMode();
const auto tabSwitchMode = customTabSwitcherMode ? customTabSwitcherMode.Value() : _settings.GlobalSettings().TabSwitcherMode();
if (tabSwitchMode == TabSwitcherMode::Disabled)
{
uint32_t tabCount = _tabs.Size();
@@ -1736,8 +1736,8 @@ namespace winrt::TerminalApp::implementation
{
ContentDialogResult warningResult = co_await _ShowCloseReadOnlyDialog();
// The primary action is canceling the action
if (warningResult == ContentDialogResult::Primary)
// If the user didn't explicitly click on close tab - leave
if (warningResult != ContentDialogResult::Primary)
{
co_return;
}

View File

@@ -182,7 +182,7 @@ namespace winrt::TerminalApp::implementation
void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term, TerminalTab& hostingTab);
void _SelectNextTab(const bool bMoveRight);
void _SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference<Microsoft::Terminal::Settings::Model::TabSwitcherMode>& customTabSwitcherMode);
bool _SelectTab(const uint32_t tabIndex);
void _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);

View File

@@ -57,7 +57,7 @@ the MIT License. See LICENSE in the project root for license information. -->
x:Load="False"
x:Name="CloseReadOnlyDialog"
x:Uid="CloseReadOnlyDialog"
DefaultButton="Primary">
DefaultButton="Close">
</ContentDialog>
<ContentDialog

View File

@@ -788,7 +788,7 @@ namespace winrt::TerminalApp::implementation
closeTabMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_rootPane->Close();
tab->_ClosedHandlers(nullptr, nullptr);
}
});
closeTabMenuItem.Text(RS_(L"TabClose"));

View File

@@ -46,6 +46,7 @@
#include "winrt/Windows.UI.Xaml.Markup.h"
#include "winrt/Windows.UI.Xaml.Documents.h"
#include "winrt/Windows.UI.Xaml.Automation.h"
#include "winrt/Windows.UI.Xaml.Automation.Peers.h"
#include "winrt/Windows.UI.ViewManagement.h"
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>

View File

@@ -151,6 +151,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::ToggleCommandPalette, ToggleCommandPaletteArgs::FromJson },
{ ShortcutAction::FindMatch, FindMatchArgs::FromJson },
{ ShortcutAction::NewWindow, NewWindowArgs::FromJson },
{ ShortcutAction::PrevTab, PrevTabArgs::FromJson },
{ ShortcutAction::NextTab, NextTabArgs::FromJson },
{ ShortcutAction::Invalid, nullptr },
};

View File

@@ -26,6 +26,8 @@
#include "FindMatchArgs.g.cpp"
#include "ToggleCommandPaletteArgs.g.cpp"
#include "NewWindowArgs.g.cpp"
#include "PrevTabArgs.g.cpp"
#include "NextTabArgs.g.cpp"
#include <LibraryResources.h>
@@ -534,4 +536,26 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
fmt::format(L"{}, {}", RS_(L"NewWindowCommandKey"), newTerminalArgsStr)
};
}
winrt::hstring PrevTabArgs::GenerateName() const
{
if (!_SwitcherMode)
{
return RS_(L"PrevTabCommandKey");
}
const auto mode = _SwitcherMode.Value() == TabSwitcherMode::MostRecentlyUsed ? L"most recently used" : L"in order";
return winrt::hstring(fmt::format(L"{}, {}", RS_(L"PrevTabCommandKey"), mode));
}
winrt::hstring NextTabArgs::GenerateName() const
{
if (!_SwitcherMode)
{
return RS_(L"NextTabCommandKey");
}
const auto mode = _SwitcherMode.Value() == TabSwitcherMode::MostRecentlyUsed ? L"most recently used" : L"in order";
return winrt::hstring(fmt::format(L"{}, {}", RS_(L"NextTabCommandKey"), mode));
}
}

View File

@@ -28,6 +28,8 @@
#include "ToggleCommandPaletteArgs.g.h"
#include "FindMatchArgs.g.h"
#include "NewWindowArgs.g.h"
#include "PrevTabArgs.g.h"
#include "NextTabArgs.g.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
#include "JsonUtils.h"
@@ -924,6 +926,71 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
};
struct PrevTabArgs : public PrevTabArgsT<PrevTabArgs>
{
PrevTabArgs() = default;
WINRT_PROPERTY(Windows::Foundation::IReference<TabSwitcherMode>, SwitcherMode, nullptr);
static constexpr std::string_view SwitcherModeKey{ "tabSwitcherMode" };
public:
hstring GenerateName() const;
bool Equals(const IActionArgs& other)
{
auto otherAsUs = other.try_as<PrevTabArgs>();
if (otherAsUs)
{
return otherAsUs->_SwitcherMode == _SwitcherMode;
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<PrevTabArgs>();
JsonUtils::GetValueForKey(json, SwitcherModeKey, args->_SwitcherMode);
return { *args, {} };
}
IActionArgs Copy() const
{
auto copy{ winrt::make_self<PrevTabArgs>() };
copy->_SwitcherMode = _SwitcherMode;
return *copy;
}
};
struct NextTabArgs : public NextTabArgsT<NextTabArgs>
{
NextTabArgs() = default;
WINRT_PROPERTY(Windows::Foundation::IReference<TabSwitcherMode>, SwitcherMode, nullptr);
static constexpr std::string_view SwitcherModeKey{ "tabSwitcherMode" };
public:
hstring GenerateName() const;
bool Equals(const IActionArgs& other)
{
auto otherAsUs = other.try_as<NextTabArgs>();
if (otherAsUs)
{
return otherAsUs->_SwitcherMode == _SwitcherMode;
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<NextTabArgs>();
JsonUtils::GetValueForKey(json, SwitcherModeKey, args->_SwitcherMode);
return { *args, {} };
}
IActionArgs Copy() const
{
auto copy{ winrt::make_self<NextTabArgs>() };
copy->_SwitcherMode = _SwitcherMode;
return *copy;
}
};
}
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation

View File

@@ -77,6 +77,13 @@ namespace Microsoft.Terminal.Settings.Model
CommandLine
};
enum TabSwitcherMode
{
MostRecentlyUsed,
InOrder,
Disabled,
};
[default_interface] runtimeclass NewTerminalArgs {
NewTerminalArgs();
NewTerminalArgs(Int32 profileIndex);
@@ -226,4 +233,14 @@ namespace Microsoft.Terminal.Settings.Model
NewWindowArgs(NewTerminalArgs terminalArgs);
NewTerminalArgs TerminalArgs { get; };
};
[default_interface] runtimeclass PrevTabArgs : IActionArgs
{
Windows.Foundation.IReference<TabSwitcherMode> SwitcherMode;
};
[default_interface] runtimeclass NextTabArgs : IActionArgs
{
Windows.Foundation.IReference<TabSwitcherMode> SwitcherMode;
};
}

View File

@@ -28,13 +28,6 @@ namespace Microsoft.Terminal.Settings.Model
MaximizedFocusMode,
};
enum TabSwitcherMode
{
MostRecentlyUsed,
InOrder,
Disabled,
};
enum WindowingMode
{
UseNew,

View File

@@ -106,4 +106,4 @@
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />
<Import Project="$(SolutionDir)src\common.build.tests.props" />
</Project>
</Project>

View File

@@ -111,6 +111,9 @@
<ClCompile Include="ObjectTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ConptyOutputTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="UnicodeLiteral.hpp">

View File

@@ -26,6 +26,7 @@
#include <memory_resource>
#include <map>
#include <mutex>
#include <numeric>
#include <shared_mutex>
#include <new>
#include <optional>
@@ -94,6 +95,7 @@
// {fmt}, a C++20-compatible formatting library
#include <fmt/format.h>
#include <fmt/compile.h>
#define USE_INTERVAL_TREE_NAMESPACE
#include <IntervalTree.h>

View File

@@ -13,6 +13,7 @@
#include "til/point.h"
#include "til/operators.h"
#include "til/rectangle.h"
#include "til/rle.h"
#include "til/bitmap.h"
#include "til/u8u16convert.h"
#include "til/spsc.h"

1161
src/inc/til/rle.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -472,9 +472,8 @@ using namespace Microsoft::Console::Render;
// send the auto-assigned ID, prefixed with the PID of this session
// (we do this so different conpty sessions do not overwrite each other's hyperlinks)
const auto sessionID = GetCurrentProcessId();
const std::string fmt{ "\x1b]8;id={}-{};{}\x1b\\" };
const std::string uri_str{ til::u16u8(uri) };
auto s = fmt::format(fmt, sessionID, numberId, uri_str);
auto s = fmt::format(FMT_COMPILE("\x1b]8;id={}-{};{}\x1b\\"), sessionID, numberId, uri_str);
return _Write(s);
}
else
@@ -482,10 +481,9 @@ using namespace Microsoft::Console::Render;
// This is the case of user-defined IDs:
// send the user-defined ID, prefixed with a "u"
// (we do this so no application can accidentally override a user defined ID)
const std::string fmt{ "\x1b]8;id=u-{};{}\x1b\\" };
const std::string uri_str{ til::u16u8(uri) };
const std::string customId_str{ til::u16u8(customId) };
auto s = fmt::format(fmt, customId_str, uri_str);
auto s = fmt::format(FMT_COMPILE("\x1b]8;id=u-{};{}\x1b\\"), customId_str, uri_str);
return _Write(s);
}
}

View File

@@ -784,7 +784,7 @@ std::wstring TerminalInput::_GenerateWin32KeySequence(const KeyEvent& key)
// Kd: the value of bKeyDown - either a '0' or '1'. If omitted, defaults to '0'.
// Cs: the value of dwControlKeyState - any number. If omitted, defaults to '0'.
// Rc: the value of wRepeatCount - any number. If omitted, defaults to '1'.
return fmt::format(L"\x1b[{};{};{};{};{};{}_",
return fmt::format(FMT_COMPILE(L"\x1b[{};{};{};{};{};{}_"),
key.GetVirtualKeyCode(),
key.GetVirtualScanCode(),
static_cast<int>(key.GetCharData()),

View File

@@ -0,0 +1,744 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "til/rle.h"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
class RunLengthEncodingTests
{
TEST_CLASS(RunLengthEncodingTests);
// NOTE: In some cases, these tests are also about ensuring that the various scenarios
// for template usage can compile correctly and will have minimal exercised functionality
// at unit test runtime.
TEST_METHOD(ConstructEmpty)
{
til::rle<unsigned int> rle;
VERIFY_ARE_EQUAL(0, rle.size());
VERIFY_ARE_EQUAL(rle.cbegin(), rle.cend());
}
TEST_METHOD(ConstructDefaultLength)
{
til::rle<unsigned int> rle(86, 9);
VERIFY_ARE_EQUAL(86, rle.size());
}
TEST_METHOD(ConstructVerySmall)
{
const til::rle<unsigned short, unsigned char> rle(12, 37);
VERIFY_ARE_EQUAL(12, rle.size());
}
TEST_METHOD(ConstructWithMinimumLoadSize)
{
const til::rle<unsigned short, unsigned short> def;
const til::rle<unsigned short, unsigned short, 3> bigger;
VERIFY_IS_GREATER_THAN(sizeof(bigger), sizeof(def));
}
TEST_METHOD(Size)
{
const til::rle<unsigned short> rle(19, 12);
VERIFY_ARE_EQUAL(19, rle.size());
}
TEST_METHOD(AtPos)
{
til::rle<int> rle(10, 10);
rle.insert(3, 0, 4);
rle.insert(7, 4, 2);
rle.insert(11, 6, 3);
rle.insert(4, 9, 1);
VERIFY_ARE_EQUAL(3, rle.at(0));
VERIFY_ARE_EQUAL(3, rle.at(1));
VERIFY_ARE_EQUAL(3, rle.at(2));
VERIFY_ARE_EQUAL(3, rle.at(3));
VERIFY_ARE_EQUAL(7, rle.at(4));
VERIFY_ARE_EQUAL(7, rle.at(5));
VERIFY_ARE_EQUAL(11, rle.at(6));
VERIFY_ARE_EQUAL(11, rle.at(7));
VERIFY_ARE_EQUAL(11, rle.at(8));
VERIFY_ARE_EQUAL(4, rle.at(9));
}
TEST_METHOD(AtPosApplies)
{
til::rle<int> rle(10, 10);
rle.insert(3, 0, 4);
rle.insert(7, 4, 2);
rle.insert(11, 6, 3);
rle.insert(4, 9, 1);
size_t appliesExpected = 4;
size_t applies = 0;
VERIFY_ARE_EQUAL(3, rle.at(0, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
--appliesExpected;
VERIFY_ARE_EQUAL(3, rle.at(1, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
--appliesExpected;
VERIFY_ARE_EQUAL(3, rle.at(2, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
--appliesExpected;
VERIFY_ARE_EQUAL(3, rle.at(3, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
appliesExpected = 2;
VERIFY_ARE_EQUAL(7, rle.at(4, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
--appliesExpected;
VERIFY_ARE_EQUAL(7, rle.at(5, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
appliesExpected = 3;
VERIFY_ARE_EQUAL(11, rle.at(6, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
--appliesExpected;
VERIFY_ARE_EQUAL(11, rle.at(7, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
--appliesExpected;
VERIFY_ARE_EQUAL(11, rle.at(8, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
appliesExpected = 1;
VERIFY_ARE_EQUAL(4, rle.at(9, applies));
VERIFY_ARE_EQUAL(appliesExpected, applies);
}
TEST_METHOD(Substr)
{
til::rle<int> rle(10, 10);
rle.insert(3, 0, 4);
rle.insert(7, 4, 2);
rle.insert(11, 6, 3);
rle.insert(4, 9, 1);
// 3 3 3 3 7 7 11 11 11 4
Log::Comment(L"1.) Nothing substring should match original.");
{
til::rle<int> expected(10, 10);
expected = rle;
// 3 3 3 3 7 7 11 11 11 4
const auto actual = rle.substr();
VERIFY_ARE_EQUAL(expected, actual);
}
Log::Comment(L"2.) Offset substring to implicit end.");
{
til::rle<int> expected(7, 10);
expected.insert(3, 0, 1);
expected.insert(7, 1, 2);
expected.insert(11, 3, 3);
expected.insert(4, 6, 1);
// 3 7 7 11 11 11 4
const auto actual = rle.substr(3);
VERIFY_ARE_EQUAL(expected, actual);
}
Log::Comment(L"3.) Substring cutting out middle bit.");
{
til::rle<int> expected(4, 4);
expected.insert(7, 0, 1);
expected.insert(11, 1, 3);
// 7 11 11 11
const auto actual = rle.substr(5, 4);
VERIFY_ARE_EQUAL(expected, actual);
}
}
TEST_METHOD(Replace)
{
til::rle<int> actual(20, 10);
actual.insert(3, 0, 4);
actual.insert(7, 4, 2);
actual.insert(11, 6, 3);
actual.insert(4, 9, 1);
actual.insert(7, 10, 5);
actual.insert(11, 15, 2);
actual.insert(9, 17, 3);
actual.replace(7, 49);
actual.replace(9, 81);
actual.replace(3, 9);
til::rle<int> expected(20, 10);
expected.insert(9, 0, 4);
expected.insert(49, 4, 2);
expected.insert(11, 6, 3);
expected.insert(4, 9, 1);
expected.insert(49, 10, 5);
expected.insert(11, 15, 2);
expected.insert(81, 17, 3);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ResizeShrink)
{
til::rle<int> actual(10, 10);
actual.insert(3, 0, 4);
actual.insert(7, 4, 2);
actual.insert(11, 6, 3);
actual.insert(4, 9, 1);
// 3 3 3 3 7 7 11 11 11 4
// 3 for 4, 7 for 2, 11 for 3, 4 for 1.
til::rle<int> expected(7, 10);
expected.insert(3, 0, 4);
expected.insert(7, 4, 2);
expected.insert(11, 6, 1);
// 3 3 3 3 7 7 11
// 3 for 4, 7 for 2, 11 for 1
actual.resize(7);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ResizeGrow)
{
til::rle<int> actual(10, 10);
actual.insert(3, 0, 4);
actual.insert(7, 4, 2);
actual.insert(11, 6, 3);
actual.insert(4, 9, 1);
// 3 3 3 3 7 7 11 11 11 4
// 3 for 4, 7 for 2, 11 for 3, 4 for 1.
til::rle<int> expected(13, 10);
expected.insert(3, 0, 4);
expected.insert(7, 4, 2);
expected.insert(11, 6, 3);
expected.insert(4, 9, 4);
// 3 3 3 3 7 7 11 11 11 4 4 4 4
// 3 for 4, 7 for 2, 11 for 3, 4 for 4.
actual.resize(13);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(FillAll)
{
til::rle<int> actual(10, 10);
actual.insert(3, 0, 4);
actual.insert(7, 4, 2);
actual.insert(11, 6, 3);
actual.insert(4, 9, 1);
actual.fill(20);
til::rle<int> expected(10, 20);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(FillFrom)
{
til::rle<int> actual(10, 10);
actual.insert(3, 0, 4);
actual.insert(7, 4, 2);
actual.insert(11, 6, 3);
actual.insert(4, 9, 1);
actual.fill(20, 2);
til::rle<int> expected(10, 20);
expected.insert(3, 0, 2);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(Insert)
{
til::rle<int> actual(10, 10);
actual.insert(4, 9); // insert single, implicit length
til::rle<int> expected(10, 4);
expected.insert(10, 0, 9); // insert multiple, say length
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(Assign)
{
til::rle<int> actual(10, 10);
// Prep initial buffer.
// 10 10 10 10 10 10 10 10 10 10
// Make something that can hold a span of pairs to assign in bulk.
std::vector<std::pair<int, size_t>> items;
items.push_back({ 400, 2 });
items.push_back({ 20, 3 });
// 400 400 20 20 20
// If we assign this to the front, we expect
// 400 400 20 20 20 10 10 10 10 10
til::rle<int> expected(10, 10);
expected.insert(400, 0, 2);
expected.insert(20, 2, 3);
actual.assign(items.cbegin(), items.cend());
VERIFY_ARE_EQUAL(expected, actual);
// Now try assigning it again part way in.
// Our new expectation building on the last one if we assign at
// index 3 would be
// 400 400 20 400 400 20 20 20 10 10
expected.insert(400, 3, 2);
expected.insert(20, 5, 3);
actual.assign(items.cbegin(), items.cend(), 3);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(Equal)
{
til::rle<int> actual(10, 10);
til::rle<int> expected(10, 10);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(NotEqualValue)
{
til::rle<int> actual(10, 9);
til::rle<int> expected(10, 10);
VERIFY_ARE_NOT_EQUAL(expected, actual);
}
TEST_METHOD(NotEqualLength)
{
til::rle<int> actual(5, 10);
til::rle<int> expected(10, 10);
VERIFY_ARE_NOT_EQUAL(expected, actual);
}
TEST_METHOD(IteratorIncDecOnes)
{
til::rle<int> rle(10, 1);
rle.insert(2, 0, 2);
rle.insert(3, 2, 3);
rle.insert(4, 5, 4);
// test array should be 2 2 3 3 3 4 4 4 4 1
// or 2 for 2, 3 for 3, 4 for 4, 1 for 1.
Log::Comment(L"Increment by 1s.");
auto it = rle.begin();
for (auto i = 0; i < 2; ++i)
{
VERIFY_ARE_EQUAL(2, *it);
++it;
}
for (auto i = 0; i < 3; ++i)
{
VERIFY_ARE_EQUAL(3, *it);
++it;
}
for (auto i = 0; i < 4; ++i)
{
VERIFY_ARE_EQUAL(4, *it);
++it;
}
for (auto i = 0; i < 1; ++i)
{
VERIFY_ARE_EQUAL(1, *it);
++it;
}
VERIFY_ARE_EQUAL(rle.end(), it);
Log::Comment(L"Decrement by 1s.");
for (auto i = 0; i < 1; ++i)
{
--it;
VERIFY_ARE_EQUAL(1, *it);
}
for (auto i = 0; i < 4; ++i)
{
--it;
VERIFY_ARE_EQUAL(4, *it);
}
for (auto i = 0; i < 3; ++i)
{
--it;
VERIFY_ARE_EQUAL(3, *it);
}
for (auto i = 0; i < 2; ++i)
{
--it;
VERIFY_ARE_EQUAL(2, *it);
}
VERIFY_ARE_EQUAL(rle.begin(), it);
}
struct TestStruct
{
int a;
int b;
[[nodiscard]] bool operator==(const TestStruct& right) const noexcept
{
return a == right.a && b == right.b;
}
};
TEST_METHOD(ConstIteratorReference)
{
const TestStruct expected{ 3, 2 };
til::rle<TestStruct> rle(5, expected);
const TestStruct actual = *rle.cbegin();
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ConstIteratorPointer)
{
const TestStruct expected{ 3, 2 };
til::rle<TestStruct> rle(5, expected);
const auto it = rle.cbegin();
const TestStruct actual{ it->a, it->b };
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ConstIteratorIncPrefix)
{
til::rle<int> rle(5, 2);
rle.insert(7, 1, 1);
// 2 7 2 2 2
auto it = rle.cbegin();
++it;
VERIFY_ARE_EQUAL(7, *it);
}
TEST_METHOD(ConstIteratorIncPostfix)
{
til::rle<int> rle(5, 2);
rle.insert(7, 1, 1);
// 2 7 2 2 2
auto it = rle.cbegin();
auto prevIt = it++;
VERIFY_ARE_EQUAL(7, *it);
VERIFY_ARE_EQUAL(2, *prevIt);
VERIFY_ARE_NOT_EQUAL(it, prevIt);
}
TEST_METHOD(ConstIteratorDecPrefix)
{
til::rle<int> rle(5, 2);
rle.insert(7, 4, 1);
// 2 2 2 2 7
auto it = rle.cend();
--it;
VERIFY_ARE_EQUAL(7, *it);
}
TEST_METHOD(ConstIteratorDecPostfix)
{
til::rle<int> rle(5, 2);
rle.insert(7, 4, 1);
// 2 2 2 2 7
auto it = rle.cend();
auto prevIt = it--;
VERIFY_ARE_EQUAL(7, *it);
VERIFY_ARE_EQUAL(rle.cend(), prevIt);
VERIFY_ARE_NOT_EQUAL(it, prevIt);
}
TEST_METHOD(ConstIteratorPlusEquals)
{
til::rle<int> rle(5, 2);
rle.insert(7, 2, 1);
// 2 2 7 2 2
auto it = rle.cbegin();
it += 2;
VERIFY_ARE_EQUAL(7, *it);
}
TEST_METHOD(ConstIteratorPlusOffset)
{
til::rle<int> rle(5, 2);
rle.insert(7, 2, 1);
// 2 2 7 2 2
auto it = rle.cbegin();
auto itAfter = it + 2;
VERIFY_ARE_EQUAL(7, *itAfter);
VERIFY_ARE_NOT_EQUAL(it, itAfter);
}
TEST_METHOD(ConstIteratorMinusEquals)
{
til::rle<int> rle(5, 2);
rle.insert(7, 3, 1);
// 2 2 2 7 2
auto it = rle.cend();
it -= 2;
VERIFY_ARE_EQUAL(7, *it);
}
TEST_METHOD(ConstIteratorMinusOffset)
{
til::rle<int> rle(5, 2);
rle.insert(7, 3, 1);
auto it = rle.cend();
auto itAfter = it - 2;
VERIFY_ARE_EQUAL(7, *itAfter);
VERIFY_ARE_NOT_EQUAL(it, itAfter);
}
TEST_METHOD(ConstIteratorDifference)
{
til::rle<int> rle(5, 2);
const ptrdiff_t expected = 5;
VERIFY_ARE_EQUAL(expected, rle.cend() - rle.cbegin());
VERIFY_ARE_EQUAL(-expected, rle.cbegin() - rle.cend());
}
TEST_METHOD(ConstIteratorArrayOffset)
{
til::rle<int> rle(5, 2);
rle.insert(7, 2, 1);
const auto it = rle.cbegin();
VERIFY_ARE_EQUAL(2, it[0]);
VERIFY_ARE_EQUAL(2, it[1]);
VERIFY_ARE_EQUAL(7, it[2]);
VERIFY_ARE_EQUAL(2, it[3]);
VERIFY_ARE_EQUAL(2, it[4]);
}
TEST_METHOD(ConstIteratorEquality)
{
til::rle<int> rle(5, 2);
auto begin = rle.cbegin();
auto end = rle.cend();
end -= 5;
VERIFY_IS_TRUE(begin == end);
}
TEST_METHOD(ConstIteratorInequality)
{
til::rle<int> rle(5, 2);
auto begin = rle.cbegin();
auto end = rle.cend();
VERIFY_IS_TRUE(begin != end);
}
TEST_METHOD(ConstIteratorLessThan)
{
til::rle<int> rle(5, 2);
auto begin = rle.cbegin();
auto end = rle.cend();
auto begin2 = end - 5;
VERIFY_IS_TRUE(begin < end);
VERIFY_IS_FALSE(end < begin);
VERIFY_IS_FALSE(begin < begin2);
}
TEST_METHOD(ConstIteratorGreaterThan)
{
til::rle<int> rle(5, 2);
auto begin = rle.cbegin();
auto end = rle.cend();
auto begin2 = end - 5;
VERIFY_IS_FALSE(begin > end);
VERIFY_IS_TRUE(end > begin);
VERIFY_IS_FALSE(begin > begin2);
}
TEST_METHOD(ConstIteratorLessThanEqual)
{
til::rle<int> rle(5, 2);
auto begin = rle.cbegin();
auto end = rle.cend();
auto begin2 = end - 5;
VERIFY_IS_TRUE(begin <= end);
VERIFY_IS_FALSE(end <= begin);
VERIFY_IS_TRUE(begin <= begin2);
}
TEST_METHOD(ConstIteratorGreaterThanEqual)
{
til::rle<int> rle(5, 2);
auto begin = rle.cbegin();
auto end = rle.cend();
auto begin2 = end - 5;
VERIFY_IS_FALSE(begin >= end);
VERIFY_IS_TRUE(end >= begin);
VERIFY_IS_TRUE(begin >= begin2);
}
TEST_METHOD(ConstReverseIterate)
{
til::rle<int> rle(5, 5);
rle.insert(1, 0, 1);
rle.insert(2, 1, 1);
rle.insert(3, 2, 1);
rle.insert(4, 3, 1);
// 1 2 3 4 5
auto rit = rle.crbegin();
for (int i = 5; i > 0; i--)
{
VERIFY_ARE_EQUAL(i, *rit);
rit++;
}
VERIFY_ARE_EQUAL(rle.crend(), rit);
}
//TEST_METHOD(NonConstIterators)
//{
// til::rle<int> rle(5, 5);
// auto iter = rle.begin();
// *iter++ = 1;
// *iter++ = 2;
// *iter++ = 3;
// *iter++ = 4;
// VERIFY_ARE_EQUAL(1, rle.at(0));
// VERIFY_ARE_EQUAL(2, rle.at(1));
// VERIFY_ARE_EQUAL(3, rle.at(2));
// VERIFY_ARE_EQUAL(4, rle.at(3));
// VERIFY_ARE_EQUAL(5, rle.at(4));
// VERIFY_ARE_EQUAL(rle.end(), iter);
// auto reverseIter = rle.crbegin();
// VERIFY_ARE_EQUAL(5, *reverseIter++);
// VERIFY_ARE_EQUAL(4, *reverseIter++);
// VERIFY_ARE_EQUAL(3, *reverseIter++);
// VERIFY_ARE_EQUAL(2, *reverseIter++);
// VERIFY_ARE_EQUAL(1, *reverseIter++);
// VERIFY_ARE_EQUAL(rle.crend(), reverseIter);
//}
TEST_METHOD(IteratorIncDecMultiple)
{
til::rle<int> rle(10, 1);
rle.insert(2, 0, 2);
rle.insert(3, 2, 3);
rle.insert(4, 5, 4);
// test array should be 2 2 3 3 3 4 4 4 4 1
// or 2 for 2, 3 for 3, 4 for 4, 1 for 1.
auto it = rle.begin();
// 2 2 3 3 3 4 4 4 4 1
// ^
VERIFY_ARE_EQUAL(2, *it, L"Check we're sitting on the first of the first run.");
// 2 2 3 3 3 4 4 4 4 1
// ^->
++it;
// 2 2 3 3 3 4 4 4 4 1
// ^
VERIFY_ARE_EQUAL(2, *it, L"Move a spot into the run and ensure we're still on the same one.");
// 2 2 3 3 3 4 4 4 4 1
// ^----->
it += 3;
// 2 2 3 3 3 4 4 4 4 1
// ^
VERIFY_ARE_EQUAL(3, *it, L"Jump forward by 3 and we should still be on the second run of 3s.");
// 2 2 3 3 3 4 4 4 4 1
// ^------->
it += 4;
// 2 2 3 3 3 4 4 4 4 1
// ^
VERIFY_ARE_EQUAL(4, *it, L"Jump forward by 4 and we should still be on the third run of 4s.");
// 2 2 3 3 3 4 4 4 4 1
// ^--->
it += 2;
// 2 2 3 3 3 4 4 4 4 1
// ^
VERIFY_ARE_EQUAL(rle.end(), it, L"Jump past the last run of 1 for 1 to what should be the end.");
// 2 2 3 3 3 4 4 4 4 1
// <-----^
it -= 3;
// 2 2 3 3 3 4 4 4 4 1
// ^
VERIFY_ARE_EQUAL(4, *it, L"Jump back by 3 and we should be in the middle of the 4s run.");
// 2 2 3 3 3 4 4 4 4 1
// <-------^
it -= 4;
// 2 2 3 3 3 4 4 4 4 1
// ^
VERIFY_ARE_EQUAL(3, *it, L"Jump back by 4 and we should be in the middle of the 3s run.");
// 2 2 3 3 3 4 4 4 4 1
// <-----^
it -= 3;
// 2 2 3 3 3 4 4 4 4 1
// ^
VERIFY_ARE_EQUAL(2, *it, L"Jump back by 3 and we should be at the beginning of the 2s run.");
VERIFY_ARE_EQUAL(rle.begin(), it, L"And it should equal 'begin'");
}
};

View File

@@ -21,6 +21,7 @@ SOURCES = \
PointTests.cpp \
MathTests.cpp \
RectangleTests.cpp \
RunLengthEncodingTests.cpp \
SizeTests.cpp \
SomeTests.cpp \
u8u16convertTests.cpp \

View File

@@ -17,6 +17,7 @@
<ClCompile Include="StaticMapTests.cpp" />
<ClCompile Include="MathTests.cpp" />
<ClCompile Include="RectangleTests.cpp" />
<ClCompile Include="RunLengthEncodingTests.cpp" />
<ClCompile Include="SizeTests.cpp" />
<ClCompile Include="ColorTests.cpp" />
<ClCompile Include="CoalesceTests.cpp" />

View File

@@ -97,4 +97,29 @@
<Type Name="til::color">
<DisplayString>{{RGB: {(int)r,d}, {(int)g,d}, {(int)b,d}; α: {(int)a,d}}}</DisplayString>
</Type>
<Type Name="til::rle&lt;*&gt;">
<DisplayString>{{Size: {_size,d}}}</DisplayString>
<Expand>
<Item Name="[size]">_size</Item>
<ArrayItems>
<Size>_list.m_holder.m_size</Size>
<ValuePointer>_list.m_holder.m_start</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="til::details::rle_const_iterator&lt;*&gt;">
<DisplayString>{{ Run of {_it.m_ptr->first,d} for {_it.m_ptr->second,d} at {_usage,d}}}</DisplayString>
</Type>
<Type Name="boost::container::small_vector&lt;*&gt;">
<Expand>
<Item Name="[size]">m_holder.m_size</Item>
<ArrayItems>
<Size>m_holder.m_size</Size>
<ValuePointer>m_holder.m_start</ValuePointer>
</ArrayItems>
</Expand>
</Type>
</AutoVisualizer>