mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 14:19:45 +00:00
Compare commits
160 Commits
dev/migrie
...
dev/duhowe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
635416ef5d | ||
|
|
4c7879bfb5 | ||
|
|
f3439e201e | ||
|
|
d3264964a9 | ||
|
|
dd2736f334 | ||
|
|
020746d93d | ||
|
|
dc6d82e9bc | ||
|
|
239b4d16b9 | ||
|
|
fb485a2b40 | ||
|
|
45a36cf83a | ||
|
|
09273be1c8 | ||
|
|
f1090a07f5 | ||
|
|
b7e537e5e7 | ||
|
|
7b9ec0ed6a | ||
|
|
06baead9ea | ||
|
|
21a62c5fef | ||
|
|
2d66dc44f5 | ||
|
|
547349af77 | ||
|
|
7ab0e982c7 | ||
|
|
165d3edde9 | ||
|
|
a3c7bc3349 | ||
|
|
772ed3a7b7 | ||
|
|
dbc27ab041 | ||
|
|
286fdfea79 | ||
|
|
decbc0f5a4 | ||
|
|
f9a36f1ff7 | ||
|
|
6a763bcd41 | ||
|
|
69728632eb | ||
|
|
3a122bf420 | ||
|
|
28d28cb469 | ||
|
|
274bbf0e44 | ||
|
|
68cce101bc | ||
|
|
58ce22d484 | ||
|
|
2668273616 | ||
|
|
a5f9c85c39 | ||
|
|
84bb98bc81 | ||
|
|
4d27a05318 | ||
|
|
86aa666903 | ||
|
|
031271f824 | ||
|
|
a5c5b8a50e | ||
|
|
c5d417fcaf | ||
|
|
b9c3b5cd1e | ||
|
|
79c47f64a2 | ||
|
|
c0e4689e77 | ||
|
|
da2b80bc0a | ||
|
|
32bf894f14 | ||
|
|
b89c8ced22 | ||
|
|
4d9a266c12 | ||
|
|
391abafc2e | ||
|
|
3c78e01ab5 | ||
|
|
4bbe3a388c | ||
|
|
62ffa4ba41 | ||
|
|
37aa29545b | ||
|
|
52cc523ed2 | ||
|
|
0eff8c06e3 | ||
|
|
437914807a | ||
|
|
d1fbbb8a83 | ||
|
|
f2eed92345 | ||
|
|
b6b1ff8b2c | ||
|
|
a7ab17571b | ||
|
|
8f346a7158 | ||
|
|
437b5ac595 | ||
|
|
feed768b3f | ||
|
|
937cadcad0 | ||
|
|
9aee510ce0 | ||
|
|
c9aeea1fdc | ||
|
|
6b4b63b18a | ||
|
|
a01500f051 | ||
|
|
c12dc2aa4d | ||
|
|
88c3ef68a5 | ||
|
|
1b09ae3b95 | ||
|
|
7aa7ce2bce | ||
|
|
0f81deb7c0 | ||
|
|
48325f9aca | ||
|
|
8695d9ee4b | ||
|
|
a798a603e1 | ||
|
|
62b34cf6f7 | ||
|
|
f25d258a43 | ||
|
|
cfdea71dc0 | ||
|
|
86928bb48d | ||
|
|
bb4711de54 | ||
|
|
23a02c5218 | ||
|
|
2119164d43 | ||
|
|
b4d37d8c70 | ||
|
|
b4fce27203 | ||
|
|
8ea3cb9972 | ||
|
|
2b851caeed | ||
|
|
aa625098ed | ||
|
|
85ca8f556c | ||
|
|
afefe693df | ||
|
|
7c8f74259d | ||
|
|
0c130fa65b | ||
|
|
3eaa781499 | ||
|
|
bbc14a0baf | ||
|
|
b674ac5c19 | ||
|
|
744ca2450a | ||
|
|
f88ef02c98 | ||
|
|
bcf2422c4d | ||
|
|
7e7a69ff7c | ||
|
|
bfd480b885 | ||
|
|
6d94fbc89f | ||
|
|
42befa7b58 | ||
|
|
97abc3df1d | ||
|
|
8ef18d3c88 | ||
|
|
1f19ed0cd2 | ||
|
|
2a5ce4900f | ||
|
|
ab04067e49 | ||
|
|
18e4e22394 | ||
|
|
33cb0eb05f | ||
|
|
d319d479c7 | ||
|
|
0022898fe8 | ||
|
|
1774cfd843 | ||
|
|
07201d6cd1 | ||
|
|
eebea5129c | ||
|
|
6033ae66c5 | ||
|
|
e4b80e2ef3 | ||
|
|
6803cfb96f | ||
|
|
8f08bb04d0 | ||
|
|
30046dd4a7 | ||
|
|
cacf66860f | ||
|
|
21a9c55752 | ||
|
|
43dbbd590f | ||
|
|
3517a6a3f1 | ||
|
|
d6ac717a48 | ||
|
|
657dd5f43e | ||
|
|
11ad04754d | ||
|
|
51c0b423fb | ||
|
|
d7e24ad6d0 | ||
|
|
a63f060f72 | ||
|
|
5608cf15a3 | ||
|
|
40bc3d7fbc | ||
|
|
00cdacc96b | ||
|
|
54dc2c4432 | ||
|
|
fc0ef37977 | ||
|
|
1da3bc7f1f | ||
|
|
2de475b36a | ||
|
|
c392ade6dd | ||
|
|
975d767d11 | ||
|
|
4f3a639c19 | ||
|
|
e1e6e662f9 | ||
|
|
97dc5c8d75 | ||
|
|
274bdb31da | ||
|
|
5027c8031d | ||
|
|
b3c9f01432 | ||
|
|
c0c284fed3 | ||
|
|
08096b2343 | ||
|
|
f79276b21b | ||
|
|
1ce22a87e6 | ||
|
|
c51bb3a7a6 | ||
|
|
6567201e0e | ||
|
|
bea13bddf1 | ||
|
|
81e2bc98d1 | ||
|
|
446ef22044 | ||
|
|
16aa79d78d | ||
|
|
e2b2d9b92c | ||
|
|
fba4e227f0 | ||
|
|
d21036d313 | ||
|
|
79ef7477f2 | ||
|
|
0973aeab15 | ||
|
|
a94e508010 |
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"XamlStyler.Console": {
|
||||
"version": "3.2008.4",
|
||||
"version": "3.2206.4",
|
||||
"commands": [
|
||||
"xstyler"
|
||||
]
|
||||
|
||||
15
.git-blame-ignore-revs
Normal file
15
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,15 @@
|
||||
# Commits mentioned in this file will automatically
|
||||
# be skipped by GitHub's blame view.
|
||||
# To use it with "git", run
|
||||
# > git blame --ignore-revs-file ./.git-blame-ignore-revs
|
||||
|
||||
# Reformatted the entire codebase
|
||||
9b92986b49bed8cc41fde4d6ef080921c41e6d9e
|
||||
|
||||
# Line Endings changes
|
||||
cb7a76d96c92aa9fc7b03f69148fb0c75dff191d
|
||||
5bbf61af8c8f12e6c05d07a696bf7d411b330a67
|
||||
d07546a6fef73fa4e1fb1c2f01535843d1fcc212
|
||||
|
||||
# UTF-8 BOM changes
|
||||
ddae2a1d49d604487d3c963e5eacbeb73861d986
|
||||
4
.github/ISSUE_TEMPLATE/Bug_Report.yml
vendored
4
.github/ISSUE_TEMPLATE/Bug_Report.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: "Bug report 🐛"
|
||||
description: Report errors or unexpected behavior
|
||||
labels: [Issue-Bug]
|
||||
labels: [Issue-Bug, Needs-Triage]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -14,7 +14,7 @@ body:
|
||||
label: Windows Terminal version
|
||||
placeholder: "1.7.3651.0"
|
||||
description: |
|
||||
You can find the version in the about dialog, or by running `wt -v` at the commandline.
|
||||
You can copy the version number from the About dialog. Open the About dialog by opening the menu with the "V" button (to the right of the "+" button that opens a new tab) and choosing About from the end of the list.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
|
||||
1
.github/actions/spelling/README.md
vendored
1
.github/actions/spelling/README.md
vendored
@@ -6,6 +6,7 @@ File | Purpose | Format | Info
|
||||
[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)
|
||||
|
||||
2
.github/actions/spelling/advice.md
vendored
2
.github/actions/spelling/advice.md
vendored
@@ -21,7 +21,7 @@ See the `README.md` in each directory for more information.
|
||||
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
|
||||
|
||||
|
||||
<details><summary>:clamp: If the flagged items are false positives</summary>
|
||||
<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).
|
||||
|
||||
19
.github/actions/spelling/allow/allow.txt
vendored
19
.github/actions/spelling/allow/allow.txt
vendored
@@ -1,7 +1,8 @@
|
||||
admins
|
||||
allcolors
|
||||
apc
|
||||
Apc
|
||||
apc
|
||||
backpressure
|
||||
breadcrumb
|
||||
breadcrumbs
|
||||
bsd
|
||||
@@ -12,18 +13,23 @@ clickable
|
||||
clig
|
||||
CMMI
|
||||
copyable
|
||||
CtrlDToClose
|
||||
cybersecurity
|
||||
dalet
|
||||
dcs
|
||||
Dcs
|
||||
dcs
|
||||
deselection
|
||||
dialytika
|
||||
diffing
|
||||
dje
|
||||
downside
|
||||
downsides
|
||||
dze
|
||||
dzhe
|
||||
DTo
|
||||
EDDB
|
||||
EDDC
|
||||
Emacspeak
|
||||
Enum'd
|
||||
Fitt
|
||||
formattings
|
||||
@@ -34,10 +40,12 @@ gantt
|
||||
gcc
|
||||
geeksforgeeks
|
||||
ghe
|
||||
github
|
||||
gje
|
||||
godbolt
|
||||
hostname
|
||||
hostnames
|
||||
https
|
||||
hyperlink
|
||||
hyperlinking
|
||||
hyperlinks
|
||||
@@ -54,9 +62,11 @@ Llast
|
||||
llvm
|
||||
Lmid
|
||||
locl
|
||||
lol
|
||||
lorem
|
||||
Lorigin
|
||||
maxed
|
||||
megathread
|
||||
minimalistic
|
||||
mkmk
|
||||
mnt
|
||||
@@ -81,6 +91,8 @@ runtimes
|
||||
shcha
|
||||
slnt
|
||||
Sos
|
||||
ssh
|
||||
stakeholders
|
||||
timeline
|
||||
timelines
|
||||
timestamped
|
||||
@@ -89,6 +101,7 @@ tokenizes
|
||||
tonos
|
||||
toolset
|
||||
tshe
|
||||
ubuntu
|
||||
uiatextrange
|
||||
UIs
|
||||
und
|
||||
@@ -97,5 +110,7 @@ versioned
|
||||
vsdevcmd
|
||||
We'd
|
||||
wildcards
|
||||
XBox
|
||||
YBox
|
||||
yeru
|
||||
zhe
|
||||
|
||||
9
.github/actions/spelling/allow/apis.txt
vendored
9
.github/actions/spelling/allow/apis.txt
vendored
@@ -32,10 +32,10 @@ DERR
|
||||
dlldata
|
||||
DNE
|
||||
DONTADDTORECENT
|
||||
DWMWA
|
||||
DWORDLONG
|
||||
DWMSBT
|
||||
DWMWA
|
||||
DWMWA
|
||||
DWORDLONG
|
||||
endfor
|
||||
ENDSESSION
|
||||
enumset
|
||||
@@ -110,8 +110,8 @@ memchr
|
||||
memicmp
|
||||
MENUCOMMAND
|
||||
MENUDATA
|
||||
MENUITEMINFOW
|
||||
MENUINFO
|
||||
MENUITEMINFOW
|
||||
mmeapi
|
||||
MOUSELEAVE
|
||||
mov
|
||||
@@ -158,8 +158,8 @@ rcx
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
roundf
|
||||
ROOTOWNER
|
||||
roundf
|
||||
RSHIFT
|
||||
SACL
|
||||
schandle
|
||||
@@ -211,6 +211,7 @@ UPDATEINIFILE
|
||||
userenv
|
||||
USEROBJECTFLAGS
|
||||
Viewbox
|
||||
virtualalloc
|
||||
wcsstr
|
||||
wcstoui
|
||||
winmain
|
||||
|
||||
1
.github/actions/spelling/allow/microsoft.txt
vendored
1
.github/actions/spelling/allow/microsoft.txt
vendored
@@ -56,6 +56,7 @@ QWORD
|
||||
regedit
|
||||
robocopy
|
||||
SACLs
|
||||
segoe
|
||||
sdkddkver
|
||||
Shobjidl
|
||||
Skype
|
||||
|
||||
5
.github/actions/spelling/allow/names.txt
vendored
5
.github/actions/spelling/allow/names.txt
vendored
@@ -23,6 +23,7 @@ Griese
|
||||
Hernan
|
||||
Howett
|
||||
Illhardt
|
||||
Imms
|
||||
iquilezles
|
||||
italo
|
||||
jantari
|
||||
@@ -34,6 +35,7 @@ KODELIFE
|
||||
Kodelife
|
||||
Kourosh
|
||||
kowalczyk
|
||||
leonardder
|
||||
leonmsft
|
||||
Lepilleur
|
||||
lhecker
|
||||
@@ -77,15 +79,18 @@ sonpham
|
||||
stakx
|
||||
talo
|
||||
thereses
|
||||
Thysell
|
||||
Walisch
|
||||
WDX
|
||||
Wellons
|
||||
Westerman
|
||||
Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
Zamor
|
||||
Zamora
|
||||
zamora
|
||||
zljubisic
|
||||
Zoey
|
||||
zorio
|
||||
Zverovich
|
||||
|
||||
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
@@ -0,0 +1,523 @@
|
||||
# marker to ignore all code on line
|
||||
^.*/\* #no-spell-check-line \*/.*$
|
||||
# marker for ignoring a comment to the end of the line
|
||||
// #no-spell-check.*$
|
||||
|
||||
# patch hunk comments
|
||||
^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .*
|
||||
# git index header
|
||||
index [0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
|
||||
|
||||
# cid urls
|
||||
(['"])cid:.*?\g{-1}
|
||||
|
||||
# data url in parens
|
||||
\(data:[^)]*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\)
|
||||
# data url in quotes
|
||||
([`'"])data:.*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
|
||||
# data url
|
||||
data:[-a-zA-Z=;:/0-9+]*,\S*
|
||||
|
||||
# mailto urls
|
||||
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
|
||||
|
||||
# magnet urls
|
||||
magnet:[?=:\w]+
|
||||
|
||||
# magnet urls
|
||||
"magnet:[^"]+"
|
||||
|
||||
# obs:
|
||||
"obs:[^"]*"
|
||||
|
||||
# The `\b` here means a break, it's the fancy way to handle urls, but it makes things harder to read
|
||||
# In this examples content, I'm using a number of different ways to match things to show various approaches
|
||||
# asciinema
|
||||
\basciinema\.org/a/[0-9a-zA-Z]+
|
||||
|
||||
# apple
|
||||
\bdeveloper\.apple\.com/[-\w?=/]+
|
||||
# Apple music
|
||||
\bembed\.music\.apple\.com/fr/playlist/usr-share/[-\w.]+
|
||||
|
||||
# appveyor api
|
||||
\bci\.appveyor\.com/api/projects/status/[0-9a-z]+
|
||||
# appveyor project
|
||||
\bci\.appveyor\.com/project/(?:[^/\s"]*/){2}builds?/\d+/job/[0-9a-z]+
|
||||
|
||||
# Amazon
|
||||
|
||||
# Amazon
|
||||
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
|
||||
# AWS S3
|
||||
\b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS execute-api
|
||||
\b[0-9a-z]{10}\.execute-api\.[-0-9a-z]+\.amazonaws\.com\b
|
||||
# AWS ELB
|
||||
\b\w+\.[-0-9a-z]+\.elb\.amazonaws\.com\b
|
||||
# AWS SNS
|
||||
\bsns\.[-0-9a-z]+.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS VPC
|
||||
vpc-\w+
|
||||
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
|
||||
# YouTube music
|
||||
\bmusic\.youtube\.com/youtubei/v1/browse(?:[?&]\w+=[-a-zA-Z0-9?&=_]*)
|
||||
# YouTube tag
|
||||
<\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"]
|
||||
# YouTube image
|
||||
\bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]*
|
||||
# Google Accounts
|
||||
\baccounts.google.com/[-_/?=.:;+%&0-9a-zA-Z]*
|
||||
# Google Analytics
|
||||
\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
|
||||
# Google APIs
|
||||
\bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+
|
||||
# Google Storage
|
||||
\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
|
||||
# Google Calendar
|
||||
\bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+
|
||||
\w+\@group\.calendar\.google\.com\b
|
||||
# Google DataStudio
|
||||
\bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|)
|
||||
# The leading `/` here is as opposed to the `\b` above
|
||||
# ... a short way to match `https://` or `http://` since most urls have one of those prefixes
|
||||
# Google Docs
|
||||
/docs\.google\.com/[a-z]+/(?:ccc\?key=\w+|(?:u/\d+|d/(?:e/|)[0-9a-zA-Z_-]+/)?(?:edit\?[-\w=#.]*|/\?[\w=&]*|))
|
||||
# Google Drive
|
||||
\bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]*
|
||||
# Google Groups
|
||||
\bgroups\.google\.com/(?:(?:forum/#!|d/)(?:msg|topics?|searchin)|a)/[^/\s"]+/[-a-zA-Z0-9$]+(?:/[-a-zA-Z0-9]+)*
|
||||
# Google Maps
|
||||
\bmaps\.google\.com/maps\?[\w&;=]*
|
||||
# Google themes
|
||||
themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
|
||||
# Google CDN
|
||||
\bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]*
|
||||
# Goo.gl
|
||||
/goo\.gl/[a-zA-Z0-9]+
|
||||
# Google Chrome Store
|
||||
\bchrome\.google\.com/webstore/detail/[-\w]*(?:/\w*|)
|
||||
# Google Books
|
||||
\bgoogle\.(?:\w{2,4})/books(?:/\w+)*\?[-\w\d=&#.]*
|
||||
# Google Fonts
|
||||
\bfonts\.(?:googleapis|gstatic)\.com/[-/?=:;+&0-9a-zA-Z]*
|
||||
# Google Forms
|
||||
\bforms\.gle/\w+
|
||||
# Google Scholar
|
||||
\bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+
|
||||
# Google Colab Research Drive
|
||||
\bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]*
|
||||
|
||||
# GitHub SHAs (api)
|
||||
\bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b
|
||||
# GitHub SHAs (markdown)
|
||||
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
|
||||
# GitHub SHAs
|
||||
\bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b
|
||||
# GitHub wiki
|
||||
\bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b
|
||||
# githubusercontent
|
||||
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
# githubassets
|
||||
\bgithubassets.com/[0-9a-f]+(?:[-/\w.]+)
|
||||
# gist github
|
||||
\bgist\.github\.com/[^/\s"]+/[0-9a-f]+
|
||||
# git.io
|
||||
\bgit\.io/[0-9a-zA-Z]+
|
||||
# GitHub JSON
|
||||
"node_id": "[-a-zA-Z=;:/0-9+]*"
|
||||
# Contributor
|
||||
\[[^\]]+\]\(https://github\.com/[^/\s"]+\)
|
||||
# GHSA
|
||||
GHSA(?:-[0-9a-z]{4}){3}
|
||||
|
||||
# GitLab commit
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
|
||||
# GitLab merge requests
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/-/merge_requests/\d+/diffs#[0-9a-f]{40}\b
|
||||
# GitLab uploads
|
||||
\bgitlab\.[^/\s"]*/uploads/[-a-zA-Z=;:/0-9+]*
|
||||
# GitLab commits
|
||||
\bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b
|
||||
|
||||
# binanace
|
||||
accounts.binance.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
|
||||
|
||||
# bitbucket diff
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+
|
||||
# bitbucket repositories commits
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
# bitbucket commits
|
||||
\bbitbucket\.org/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
|
||||
# bit.ly
|
||||
\bbit\.ly/\w+
|
||||
|
||||
# bitrise
|
||||
\bapp\.bitrise\.io/app/[0-9a-f]*/[\w.?=&]*
|
||||
|
||||
# bootstrapcdn.com
|
||||
\bbootstrapcdn\.com/[-./\w]+
|
||||
|
||||
# cdn.cloudflare.com
|
||||
\bcdnjs\.cloudflare\.com/[./\w]+
|
||||
|
||||
# circleci
|
||||
\bcircleci\.com/gh(?:/[^/\s"]+){1,5}.[a-z]+\?[-0-9a-zA-Z=&]+
|
||||
|
||||
# gitter
|
||||
\bgitter\.im(?:/[^/\s"]+){2}\?at=[0-9a-f]+
|
||||
|
||||
# gravatar
|
||||
\bgravatar\.com/avatar/[0-9a-f]+
|
||||
|
||||
# ibm
|
||||
[a-z.]*ibm\.com/[-_#=:%!?~.\\/\d\w]*
|
||||
|
||||
# imgur
|
||||
\bimgur\.com/[^.]+
|
||||
|
||||
# Internet Archive
|
||||
\barchive\.org/web/\d+/(?:[-\w.?,'/\\+&%$#_:]*)
|
||||
|
||||
# discord
|
||||
/discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,}
|
||||
|
||||
# Disqus
|
||||
\bdisqus\.com/[-\w/%.()!?&=_]*
|
||||
|
||||
# medium link
|
||||
\blink\.medium\.com/[a-zA-Z0-9]+
|
||||
# medium
|
||||
\bmedium\.com/\@?[^/\s"]+/[-\w]+
|
||||
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
|
||||
# powerbi
|
||||
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
|
||||
# vs devops
|
||||
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
|
||||
# microsoft store
|
||||
\bmicrosoft\.com/store/apps/\w+
|
||||
|
||||
# mvnrepository.com
|
||||
\bmvnrepository\.com/[-0-9a-z./]+
|
||||
|
||||
# now.sh
|
||||
/[0-9a-z-.]+\.now\.sh\b
|
||||
|
||||
# oracle
|
||||
\bdocs\.oracle\.com/[-0-9a-zA-Z./_?#&=]*
|
||||
|
||||
# chromatic.com
|
||||
/\S+.chromatic.com\S*[")]
|
||||
|
||||
# codacy
|
||||
\bapi\.codacy\.com/project/badge/Grade/[0-9a-f]+
|
||||
|
||||
# compai
|
||||
\bcompai\.pub/v1/png/[0-9a-f]+
|
||||
|
||||
# mailgun api
|
||||
\.api\.mailgun\.net/v3/domains/[0-9a-z]+\.mailgun.org/messages/[0-9a-zA-Z=@]*
|
||||
# mailgun
|
||||
\b[0-9a-z]+.mailgun.org
|
||||
|
||||
# /message-id/
|
||||
/message-id/[-\w@./%]+
|
||||
|
||||
# Reddit
|
||||
\breddit\.com/r/[/\w_]*
|
||||
|
||||
# requestb.in
|
||||
\brequestb\.in/[0-9a-z]+
|
||||
|
||||
# sched
|
||||
\b[a-z0-9]+\.sched\.com\b
|
||||
|
||||
# Slack url
|
||||
slack://[a-zA-Z0-9?&=]+
|
||||
# Slack
|
||||
\bslack\.com/[-0-9a-zA-Z/_~?&=.]*
|
||||
# Slack edge
|
||||
\bslack-edge\.com/[-a-zA-Z0-9?&=%./]+
|
||||
# Slack images
|
||||
\bslack-imgs\.com/[-a-zA-Z0-9?&=%.]+
|
||||
|
||||
# shields.io
|
||||
\bshields\.io/[-\w/%?=&.:+;,]*
|
||||
|
||||
# stackexchange -- https://stackexchange.com/feeds/sites
|
||||
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
|
||||
|
||||
# Sentry
|
||||
[0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b
|
||||
|
||||
# Twitter markdown
|
||||
\[\@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\)
|
||||
# Twitter hashtag
|
||||
\btwitter\.com/hashtag/[\w?_=&]*
|
||||
# Twitter status
|
||||
\btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)
|
||||
# Twitter profile images
|
||||
\btwimg\.com/profile_images/[_\w./]*
|
||||
# Twitter media
|
||||
\btwimg\.com/media/[-_\w./?=]*
|
||||
# Twitter link shortened
|
||||
\bt\.co/\w+
|
||||
|
||||
# facebook
|
||||
\bfburl\.com/[0-9a-z_]+
|
||||
# facebook CDN
|
||||
\bfbcdn\.net/[\w/.,]*
|
||||
# facebook watch
|
||||
\bfb\.watch/[0-9A-Za-z]+
|
||||
|
||||
# dropbox
|
||||
\bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+
|
||||
|
||||
# ipfs protocol
|
||||
ipfs://[0-9a-z]*
|
||||
# ipfs url
|
||||
/ipfs/[0-9a-z]*
|
||||
|
||||
# w3
|
||||
\bw3\.org/[-0-9a-zA-Z/#.]+
|
||||
|
||||
# loom
|
||||
\bloom\.com/embed/[0-9a-f]+
|
||||
|
||||
# regex101
|
||||
\bregex101\.com/r/[^/\s"]+/\d+
|
||||
|
||||
# figma
|
||||
\bfigma\.com/file(?:/[0-9a-zA-Z]+/)+
|
||||
|
||||
# freecodecamp.org
|
||||
\bfreecodecamp\.org/[-\w/.]+
|
||||
|
||||
# image.tmdb.org
|
||||
\bimage\.tmdb\.org/[/\w.]+
|
||||
|
||||
# mermaid
|
||||
\bmermaid\.ink/img/[-\w]+|\bmermaid-js\.github\.io/mermaid-live-editor/#/edit/[-\w]+
|
||||
|
||||
# Wikipedia
|
||||
\ben\.wikipedia\.org/wiki/[-\w%.#]+
|
||||
|
||||
# gitweb
|
||||
[^"\s]+/gitweb/\S+;h=[0-9a-f]+
|
||||
|
||||
# HyperKitty lists
|
||||
/archives/list/[^@/]+\@[^/\s"]*/message/[^/\s"]*/
|
||||
|
||||
# lists
|
||||
/thread\.html/[^"\s]+
|
||||
|
||||
# list-management
|
||||
\blist-manage\.com/subscribe(?:[?&](?:u|id)=[0-9a-f]+)+
|
||||
|
||||
# kubectl.kubernetes.io/last-applied-configuration
|
||||
"kubectl.kubernetes.io/last-applied-configuration": ".*"
|
||||
|
||||
# pgp
|
||||
\bgnupg\.net/pks/lookup[?&=0-9a-zA-Z]*
|
||||
|
||||
# Spotify
|
||||
\bopen\.spotify\.com/embed/playlist/\w+
|
||||
|
||||
# Mastodon
|
||||
\bmastodon\.[-a-z.]*/(?:media/|\@)[?&=0-9a-zA-Z_]*
|
||||
|
||||
# scastie
|
||||
\bscastie\.scala-lang\.org/[^/]+/\w+
|
||||
|
||||
# images.unsplash.com
|
||||
\bimages\.unsplash\.com/(?:(?:flagged|reserve)/|)[-\w./%?=%&.;]+
|
||||
|
||||
# pastebin
|
||||
\bpastebin\.com/[\w/]+
|
||||
|
||||
# heroku
|
||||
\b\w+\.heroku\.com/source/archive/\w+
|
||||
|
||||
# quip
|
||||
\b\w+\.quip\.com/\w+(?:(?:#|/issues/)\w+)?
|
||||
|
||||
# badgen.net
|
||||
\bbadgen\.net/badge/[^")\]'\s]+
|
||||
|
||||
# statuspage.io
|
||||
\w+\.statuspage\.io\b
|
||||
|
||||
# media.giphy.com
|
||||
\bmedia\.giphy\.com/media/[^/]+/[\w.?&=]+
|
||||
|
||||
# tinyurl
|
||||
\btinyurl\.com/\w+
|
||||
|
||||
# getopts
|
||||
\bgetopts\s+(?:"[^"]+"|'[^']+')
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
|
||||
|
||||
# URL escaped characters
|
||||
\%[0-9A-F][A-F]
|
||||
# IPv6
|
||||
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
|
||||
# c99 hex digits (not the full format, just one I've seen)
|
||||
0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP]
|
||||
# Punycode
|
||||
\bxn--[-0-9a-z]+
|
||||
# sha
|
||||
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
|
||||
# sha-... -- uses a fancy capture
|
||||
(['"]|")[0-9a-f]{40,}\g{-1}
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
# hex in url queries
|
||||
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
|
||||
# ssh
|
||||
(?:ssh-\S+|-nistp256) [-a-zA-Z=;:/0-9+]{12,}
|
||||
|
||||
# PGP
|
||||
\b(?:[0-9A-F]{4} ){9}[0-9A-F]{4}\b
|
||||
# GPG keys
|
||||
\b(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}\b
|
||||
# Well known gpg keys
|
||||
.well-known/openpgpkey/[\w./]+
|
||||
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
# integrity
|
||||
integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}"
|
||||
|
||||
# https://www.gnu.org/software/groff/manual/groff.html
|
||||
# man troff content
|
||||
\\f[BCIPR]
|
||||
# '
|
||||
\\\(aq
|
||||
|
||||
# .desktop mime types
|
||||
^MimeTypes?=.*$
|
||||
# .desktop localized entries
|
||||
^[A-Z][a-z]+\[[a-z]+\]=.*$
|
||||
# Localized .desktop content
|
||||
Name\[[^\]]+\]=.*
|
||||
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# crypt
|
||||
"\$2[ayb]\$.{56}"
|
||||
|
||||
# scrypt / argon
|
||||
\$(?:scrypt|argon\d+[di]*)\$\S+
|
||||
|
||||
# Input to GitHub JSON
|
||||
content: "[-a-zA-Z=;:/0-9+]*="
|
||||
|
||||
# Python stringprefix / binaryprefix
|
||||
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
|
||||
(?<!')\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
|
||||
|
||||
# Regular expressions for (P|p)assword
|
||||
\([A-Z]\|[a-z]\)[a-z]+
|
||||
|
||||
# JavaScript regular expressions
|
||||
# javascript test regex
|
||||
/.*/[gim]*\.test\(
|
||||
# javascript match regex
|
||||
\.match\(/[^/\s"]*/[gim]*\s*
|
||||
# javascript match regex
|
||||
\.match\(/\\[b].*?/[gim]*\s*\)(?:;|$)
|
||||
# javascript regex
|
||||
^\s*/\\[b].*/[gim]*\s*(?:\)(?:;|$)|,$)
|
||||
# javascript replace regex
|
||||
\.replace\(/[^/\s"]*/[gim]*\s*,
|
||||
|
||||
# Go regular expressions
|
||||
regexp?\.MustCompile\(`[^`]*`\)
|
||||
|
||||
# sed regular expressions
|
||||
sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2}
|
||||
|
||||
# go install
|
||||
go install(?:\s+[a-z]+\.[-@\w/.]+)+
|
||||
|
||||
# kubernetes pod status lists
|
||||
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
|
||||
|
||||
# kubectl - pods in CrashLoopBackOff
|
||||
\w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+
|
||||
|
||||
# kubernetes object suffix
|
||||
-[0-9a-f]{10}-\w{5}\s
|
||||
|
||||
# posthog secrets
|
||||
posthog\.init\((['"])phc_[^"',]+\g{-1},
|
||||
|
||||
# xcode
|
||||
|
||||
# xcodeproject scenes
|
||||
(?:Controller|ID|id)="\w{3}-\w{2}-\w{3}"
|
||||
|
||||
# xcode api botches
|
||||
customObjectInstantitationMethod
|
||||
|
||||
# font awesome classes
|
||||
\.fa-[-a-z0-9]+
|
||||
|
||||
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
|
||||
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
|
||||
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
|
||||
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
|
||||
## You could manually change `(?i)X...` to use `[Xx]...`
|
||||
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
|
||||
# Lorem
|
||||
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
|
||||
|
||||
# Non-English
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
|
||||
|
||||
# French
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# latex
|
||||
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
|
||||
|
||||
# the negative lookahead here is to allow catching 'templatesz' as a misspelling
|
||||
# but to otherwise recognize a Windows path with \templates\foo.template or similar:
|
||||
\\(?:necessary|r(?:eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z])
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
# Note that the next example is no longer necessary if you are using
|
||||
# to match a string starting with a `#`, use a character-class:
|
||||
[#]backwards
|
||||
# version suffix <word>v#
|
||||
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
|
||||
# Compiler flags (Scala)
|
||||
(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
# Compiler flags
|
||||
#(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# Compiler flags (linker)
|
||||
,-B
|
||||
# curl arguments
|
||||
\b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
|
||||
# set arguments
|
||||
\bset(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
|
||||
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
|
||||
# macOS temp folders
|
||||
/var/folders/\w\w/[+\w]+/(?:T|-Caches-)/
|
||||
38
.github/actions/spelling/excludes.txt
vendored
38
.github/actions/spelling/excludes.txt
vendored
@@ -2,14 +2,14 @@
|
||||
(?:(?i)\.png$)
|
||||
(?:^|/)(?i)COPYRIGHT
|
||||
(?:^|/)(?i)LICEN[CS]E
|
||||
(?:^|/)3rdparty/
|
||||
(?:^|/)dirs$
|
||||
(?:^|/)go\.mod$
|
||||
(?:^|/)go\.sum$
|
||||
(?:^|/)package(?:-lock|)\.json$
|
||||
(?:^|/)sources(?:|\.dep)$
|
||||
(?:^|/)vendor/
|
||||
ignore$
|
||||
SUMS$
|
||||
\.a$
|
||||
\.ai$
|
||||
\.avi$
|
||||
\.bmp$
|
||||
@@ -20,6 +20,8 @@ SUMS$
|
||||
\.crt$
|
||||
\.csr$
|
||||
\.dll$
|
||||
\.docx?$
|
||||
\.drawio$
|
||||
\.DS_Store$
|
||||
\.eot$
|
||||
\.eps$
|
||||
@@ -31,6 +33,7 @@ SUMS$
|
||||
\.icns$
|
||||
\.ico$
|
||||
\.jar$
|
||||
\.jks$
|
||||
\.jpeg$
|
||||
\.jpg$
|
||||
\.key$
|
||||
@@ -41,6 +44,7 @@ SUMS$
|
||||
\.mod$
|
||||
\.mp3$
|
||||
\.mp4$
|
||||
\.o$
|
||||
\.ocf$
|
||||
\.otf$
|
||||
\.pbxproj$
|
||||
@@ -48,22 +52,41 @@ SUMS$
|
||||
\.pem$
|
||||
\.png$
|
||||
\.psd$
|
||||
\.pyc$
|
||||
\.runsettings$
|
||||
\.s$
|
||||
\.sig$
|
||||
\.so$
|
||||
\.svg$
|
||||
\.svgz$
|
||||
\.svgz?$
|
||||
\.tar$
|
||||
\.tgz$
|
||||
\.tiff?$
|
||||
\.ttf$
|
||||
\.vsdx$
|
||||
\.wav$
|
||||
\.webm$
|
||||
\.webp$
|
||||
\.woff
|
||||
\.woff2?$
|
||||
\.xcf$
|
||||
\.xls
|
||||
\.xlsx?$
|
||||
\.xpm$
|
||||
\.yml$
|
||||
\.zip$
|
||||
^\.github/actions/spelling/
|
||||
^\.github/fabricbot.json$
|
||||
^\.gitignore$
|
||||
^\Q.git-blame-ignore-revs\E$
|
||||
^\Q.github/workflows/spelling.yml\E$
|
||||
^\Qdoc/reference/windows-terminal-logo.ans\E$
|
||||
^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$
|
||||
^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$
|
||||
^\Qsrc/host/ft_host/chafa.txt\E$
|
||||
^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$
|
||||
^\XamlStyler.json$
|
||||
^build/config/
|
||||
^consolegit2gitfilters\.json$
|
||||
^dep/
|
||||
@@ -90,12 +113,5 @@ SUMS$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^src/types/ut_types/UtilsTests.cpp$
|
||||
^tools/ReleaseEngineering/ServicingPipeline.ps1$
|
||||
^\.github/actions/spelling/
|
||||
^\.github/fabricbot.json$
|
||||
^\.gitignore$
|
||||
^\Q.github/workflows/spelling.yml\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$
|
||||
ignore$
|
||||
SUMS$
|
||||
|
||||
7
.github/actions/spelling/expect/alphabet.txt
vendored
7
.github/actions/spelling/expect/alphabet.txt
vendored
@@ -5,26 +5,19 @@ AAAAAABBBBBBCCC
|
||||
AAAAABBBBBBCCC
|
||||
abcd
|
||||
abcd
|
||||
abcde
|
||||
abcdef
|
||||
ABCDEFG
|
||||
ABCDEFGH
|
||||
ABCDEFGHIJ
|
||||
abcdefghijk
|
||||
ABCDEFGHIJKLMNO
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
ABCG
|
||||
ABE
|
||||
abf
|
||||
BBBBB
|
||||
BBBBBBBB
|
||||
BBBBBBBBBBBBBBDDDD
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBGGRR
|
||||
CCE
|
||||
EFG
|
||||
EFGh
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
|
||||
707
.github/actions/spelling/expect/expect.txt
vendored
707
.github/actions/spelling/expect/expect.txt
vendored
File diff suppressed because it is too large
Load Diff
2
.github/actions/spelling/expect/web.txt
vendored
2
.github/actions/spelling/expect/web.txt
vendored
@@ -1,5 +1,3 @@
|
||||
http
|
||||
www
|
||||
WCAG
|
||||
winui
|
||||
appshellintegration
|
||||
|
||||
30
.github/actions/spelling/line_forbidden.patterns
vendored
30
.github/actions/spelling/line_forbidden.patterns
vendored
@@ -1,3 +1,11 @@
|
||||
# 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
|
||||
|
||||
@@ -16,6 +24,12 @@
|
||||
# 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
|
||||
|
||||
@@ -27,10 +41,22 @@
|
||||
\b[Nn]o[nt][- ]existent\b
|
||||
|
||||
# s.b. preexisting
|
||||
[Pp]re-existing
|
||||
[Pp]re[- ]existing
|
||||
|
||||
# s.b. preempt
|
||||
[Pp]re[- ]empt\b
|
||||
|
||||
# s.b. preemptively
|
||||
[Pp]re-emptively
|
||||
[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
|
||||
|
||||
57
.github/actions/spelling/patterns/patterns.txt
vendored
57
.github/actions/spelling/patterns/patterns.txt
vendored
@@ -27,13 +27,68 @@ ROY\sG\.\sBIV
|
||||
# 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
|
||||
(?:[\\@](?: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
|
||||
|
||||
|
||||
27
.github/actions/spelling/reject.txt
vendored
27
.github/actions/spelling/reject.txt
vendored
@@ -1,31 +1,12 @@
|
||||
benefitting
|
||||
occurences?
|
||||
Sorce
|
||||
^attache$
|
||||
^attacher$
|
||||
^attachers$
|
||||
benefitting
|
||||
occurences?
|
||||
^dependan.*
|
||||
^oer$
|
||||
^spae$
|
||||
^spae-man$
|
||||
^spaebook$
|
||||
^spaecraft$
|
||||
^spaed$
|
||||
^spaedom$
|
||||
^spaeing$
|
||||
^spaeings$
|
||||
^spaeman$
|
||||
^spaer$
|
||||
^Spaerobee$
|
||||
^spaes$
|
||||
^spaewife$
|
||||
^spaewoman$
|
||||
^spaework$
|
||||
^spaewright$
|
||||
Sorce
|
||||
^[Ss]pae.*
|
||||
^untill$
|
||||
^untilling$
|
||||
^wether$
|
||||
^wether.*
|
||||
^wethers$
|
||||
^wetherteg$
|
||||
^[Ss]pae.*
|
||||
|
||||
2
.github/workflows/addToProject.yml
vendored
2
.github/workflows/addToProject.yml
vendored
@@ -5,7 +5,9 @@ on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
- unlabeled
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to project
|
||||
|
||||
106
.github/workflows/spelling2.yml
vendored
106
.github/workflows/spelling2.yml
vendored
@@ -1,10 +1,57 @@
|
||||
# spelling.yml is blocked per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-g86g-chm8-7r2p
|
||||
name: Spell checking
|
||||
|
||||
# Comment management is handled through a secondary job, for details see:
|
||||
# https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions
|
||||
#
|
||||
# `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# (in odd cases, it might actually run just to collapse a commment, but that's fairly rare)
|
||||
# it needs `contents: write` in order to add a comment.
|
||||
#
|
||||
# `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# or collapse a comment (in the case where it had previously made a comment and now no longer needs to show a comment)
|
||||
# it needs `pull-requests: write` in order to manipulate those comments.
|
||||
|
||||
# Updating pull request branches is managed via comment handling.
|
||||
# For details, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-expect-list
|
||||
#
|
||||
# These elements work together to make it happen:
|
||||
#
|
||||
# `on.issue_comment`
|
||||
# This event listens to comments by users asking to update the metadata.
|
||||
#
|
||||
# `jobs.update`
|
||||
# This job runs in response to an issue_comment and will push a new commit
|
||||
# to update the spelling metadata.
|
||||
#
|
||||
# `with.experimental_apply_changes_via_bot`
|
||||
# Tells the action to support and generate messages that enable it
|
||||
# to make a commit to update the spelling metadata.
|
||||
#
|
||||
# `with.ssh_key`
|
||||
# In order to trigger workflows when the commit is made, you can provide a
|
||||
# secret (typically, a write-enabled github deploy key).
|
||||
#
|
||||
# For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
push:
|
||||
branches: ["**"]
|
||||
tags-ignore: ["**"]
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**"
|
||||
types:
|
||||
- 'opened'
|
||||
- 'reopened'
|
||||
- 'synchronize'
|
||||
issue_comment:
|
||||
types:
|
||||
- 'created'
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
@@ -24,23 +71,64 @@ jobs:
|
||||
steps:
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@v0.0.20
|
||||
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:
|
||||
name: Report
|
||||
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
|
||||
pull-requests: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name != 'push'
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@v0.0.20
|
||||
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 }}
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,6 +1,3 @@
|
||||
[submodule "dep/gsl"]
|
||||
path = dep/gsl
|
||||
url = https://github.com/microsoft/gsl
|
||||
[submodule "dep/wil"]
|
||||
path = dep/wil
|
||||
url = https://github.com/microsoft/wil
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"Microsoft.Net.Component.4.5.TargetingPack",
|
||||
"Microsoft.VisualStudio.Component.DiagnosticTools",
|
||||
"Microsoft.VisualStudio.Component.Debugger.JustInTime",
|
||||
"Microsoft.VisualStudio.Component.Windows11SDK.22000",
|
||||
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
|
||||
"Microsoft.VisualStudio.Component.VC.CoreIde",
|
||||
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
|
||||
|
||||
@@ -101,7 +101,7 @@ If you don't have any additional info/context to add but would like to indicate
|
||||
|
||||
If you're able & willing to help fix issues and/or implement features, we'd love your contribution!
|
||||
|
||||
The best place to start is the list of ["Easy Starter"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22+label%3A%22Easy+Starter%22+) issues. These are bugs or tasks that we on the team believe would be easier to implement for someone without any prior experience in the codebase. Once you're feeling more comfortable in the codebase, feel free to just use the ["Help Wanted"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22+) label, or just find an issue your interested in and hop in!
|
||||
The best place to start is the list of ["good first issue"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22++label%3A%22good+first+issue%22+)s. These are bugs or tasks that we on the team believe would be easier to implement for someone without any prior experience in the codebase. Once you're feeling more comfortable in the codebase, feel free to just use the ["Help Wanted"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22+) label, or just find an issue you're interested in and hop in!
|
||||
|
||||
Generally, we categorize issues in the following way, which is largely derived from our old internal work tracking system:
|
||||
* ["Bugs"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Issue-Bug%22+) are parts of the Terminal & Console that are not quite working the right way. There's code to already support some scenario, but it's not quite working right. Fixing these is generally a matter of debugging the broken functionality and fixing the wrong code.
|
||||
|
||||
744
OpenConsole.sln
744
OpenConsole.sln
File diff suppressed because it is too large
Load Diff
@@ -67,7 +67,7 @@ the latest Terminal release by installing the `Microsoft.WindowsTerminal`
|
||||
package:
|
||||
|
||||
```powershell
|
||||
winget install --id=Microsoft.WindowsTerminal -e
|
||||
winget install --id Microsoft.WindowsTerminal -e
|
||||
```
|
||||
|
||||
#### Via Chocolatey (unofficial)
|
||||
@@ -289,7 +289,7 @@ If you would like to ask a question that you feel doesn't warrant an issue
|
||||
app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development)
|
||||
to locally install and run Windows Terminal
|
||||
* You must have [PowerShell 7 or later](https://github.com/PowerShell/PowerShell/releases/latest) installed
|
||||
* You must have the [Windows 11 (10.0.22000.0)
|
||||
* You must have the [Windows 11 (10.0.22621.0)
|
||||
SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/)
|
||||
installed
|
||||
* You must have at least [VS
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"msbuild-sdks": {
|
||||
"Microsoft.DotNet.Helix.Sdk": "5.0.0-beta.20277.5"
|
||||
"Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.22525.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
<!-- Mandatory. Name of the NuGet package which will contain PGO databases for consumption by build system. -->
|
||||
<PGOPackageName>Microsoft.Internal.Windows.Terminal.PGODatabase</PGOPackageName>
|
||||
|
||||
<!-- Mandatory. Major version number of the PGO database which should match the version of the product. This can be hardcoded or obtained from other sources in build system. -->
|
||||
<!-- Mandatory. Major version number of the PGO database which should match the version of the product. This can be hard-coded or obtained from other sources in build system. -->
|
||||
<PGOPackageVersionMajor>$(VersionMajor)</PGOPackageVersionMajor>
|
||||
|
||||
<!-- Mandatory. Minor version number of the PGO database which should match the version of the product. This can be hardcoded or obtained from other sources in build system. -->
|
||||
<!-- Mandatory. Minor version number of the PGO database which should match the version of the product. This can be hard-coded or obtained from other sources in build system. -->
|
||||
<PGOPackageVersionMinor>$(VersionMinor)</PGOPackageVersionMinor>
|
||||
|
||||
<!-- Mandatory, defaults to 0. Patch version number of the PGO database which should match the version of the product. This can be hardcoded or obtained from other sources in build system. -->
|
||||
<!-- Mandatory, defaults to 0. Patch version number of the PGO database which should match the version of the product. This can be hard-coded or obtained from other sources in build system. -->
|
||||
<PGOPackageVersionPatch>0</PGOPackageVersionPatch>
|
||||
|
||||
<!-- Optional, defaults to empty. Prerelease version number of the PGO database which should match the version of the product. This can be hardcoded or obtained from other sources in build system. -->
|
||||
<!-- Optional, defaults to empty. Prerelease version number of the PGO database which should match the version of the product. This can be hard-coded or obtained from other sources in build system. -->
|
||||
<PGOPackageVersionPrerelease></PGOPackageVersionPrerelease>
|
||||
|
||||
<!-- Mandatory. Path to nuget.config file for the project. Path is relative to where the props file will be. -->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
jobs:
|
||||
- job: CodeFormatCheck
|
||||
displayName: Proper Code Formatting Check
|
||||
pool: { vmImage: windows-2019 }
|
||||
pool: { vmImage: windows-2022 }
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
|
||||
@@ -43,9 +43,9 @@ jobs:
|
||||
filename: 'set'
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: 'Use NuGet 5.2.0'
|
||||
displayName: 'Use NuGet 6.3.0'
|
||||
inputs:
|
||||
versionSpec: 5.2.0
|
||||
versionSpec: 6.3.0
|
||||
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: 'NuGet restore build/Helix/packages.config'
|
||||
|
||||
@@ -27,9 +27,9 @@ jobs:
|
||||
displayName: 'Retrieve VC tools directory'
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: 'Use NuGet 5.2.0'
|
||||
displayName: 'Use NuGet 6.3.0'
|
||||
inputs:
|
||||
versionSpec: 5.2.0
|
||||
versionSpec: 6.3.0
|
||||
|
||||
- task: NuGetAuthenticate@0
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
steps:
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: 'Use NuGet 5.2.0'
|
||||
displayName: 'Use NuGet 6.3.0'
|
||||
inputs:
|
||||
versionSpec: 5.2.0
|
||||
versionSpec: 6.3.0
|
||||
|
||||
- task: NuGetAuthenticate@0
|
||||
|
||||
- script: |-
|
||||
echo ##vso[task.setvariable variable=NUGET_RESTORE_MSBUILD_ARGS]/p:Platform=$(BuildPlatform)
|
||||
displayName: Ensure NuGet restores for $(BuildPlatform)
|
||||
|
||||
# In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous.
|
||||
# This should be `task: NuGetCommand@2`
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
|
||||
@@ -22,7 +22,7 @@ Param(
|
||||
[Parameter(HelpMessage="Path to makeappx.exe")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x86\MakeAppx.exe"
|
||||
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe"
|
||||
)
|
||||
|
||||
If ($null -Eq (Get-Item $MakeAppxPath -EA:SilentlyContinue)) {
|
||||
|
||||
@@ -8,7 +8,7 @@ Param(
|
||||
[Parameter(HelpMessage="Path to Windows Kit")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0"
|
||||
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
@@ -13,6 +13,10 @@ Licensed under the MIT license.
|
||||
#define CIS_EVENT_TYPE_FOCUS (1)
|
||||
#define CIS_EVENT_TYPE_FOCUS_ACK (2)
|
||||
|
||||
#define CIS_MSG_TYPE_MAPVIRTUALKEY (0)
|
||||
#define CIS_MSG_TYPE_VKKEYSCAN (1)
|
||||
#define CIS_MSG_TYPE_GETKEYSTATE (2)
|
||||
|
||||
#define CIS_MSG_TYPE_GETDISPLAYSIZE (3)
|
||||
#define CIS_MSG_TYPE_GETFONTSIZE (4)
|
||||
#define CIS_MSG_TYPE_SETCURSOR (5)
|
||||
@@ -31,6 +35,22 @@ typedef struct {
|
||||
UCHAR Type;
|
||||
|
||||
union {
|
||||
struct {
|
||||
UINT Code;
|
||||
UINT MapType;
|
||||
UINT ReturnValue;
|
||||
} MapVirtualKeyParams;
|
||||
|
||||
struct {
|
||||
WCHAR Character;
|
||||
SHORT ReturnValue;
|
||||
} VkKeyScanParams;
|
||||
|
||||
struct {
|
||||
int VirtualKey;
|
||||
SHORT ReturnValue;
|
||||
} GetKeyStateParams;
|
||||
|
||||
struct {
|
||||
CD_IO_DISPLAY_SIZE DisplaySize;
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
[Amalgamated](https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated)
|
||||
from source commit
|
||||
[6aba23f](https://github.com/open-source-parsers/jsoncpp/commit/6aba23f4a8628d599a9ef7fa4811c4ff6e4070e2),
|
||||
release 1.9.3.
|
||||
[5defb4e](https://github.com/open-source-parsers/jsoncpp/commit/5defb4ed1a4293b8e2bf641e16b156fb9de498cc),
|
||||
release 1.9.5.
|
||||
|
||||
> Generating amalgamated source and header JsonCpp is provided with a script to
|
||||
> generate a single header and a single source file to ease inclusion into an
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/open-source-parsers/jsoncpp",
|
||||
"commitHash": "6aba23f4a8628d599a9ef7fa4811c4ff6e4070e2"
|
||||
"commitHash": "5defb4ed1a4293b8e2bf641e16b156fb9de498cc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,28 +7,28 @@
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
The JsonCpp library's source code, including accompanying documentation,
|
||||
The JsonCpp library's source code, including accompanying documentation,
|
||||
tests and demonstration applications, are licensed under the following
|
||||
conditions...
|
||||
|
||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||
this software is released into the Public Domain.
|
||||
|
||||
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
|
||||
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
|
||||
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
|
||||
|
||||
In jurisdictions which recognize Public Domain property, the user of this
|
||||
software may choose to accept it either as 1) Public Domain, 2) under the
|
||||
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||
In jurisdictions which recognize Public Domain property, the user of this
|
||||
software may choose to accept it either as 1) Public Domain, 2) under the
|
||||
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||
Public Domain/MIT License conditions described here, as they choose.
|
||||
|
||||
The MIT License is about as close to Public Domain as a license can get, and is
|
||||
described in clear, concise terms at:
|
||||
|
||||
http://en.wikipedia.org/wiki/MIT_License
|
||||
|
||||
|
||||
The full text of the MIT License follows:
|
||||
|
||||
========================================================================
|
||||
@@ -94,10 +94,10 @@ license you like.
|
||||
// 3. /CMakeLists.txt
|
||||
// IMPORTANT: also update the SOVERSION!!
|
||||
|
||||
#define JSONCPP_VERSION_STRING "1.9.3"
|
||||
#define JSONCPP_VERSION_STRING "1.9.5"
|
||||
#define JSONCPP_VERSION_MAJOR 1
|
||||
#define JSONCPP_VERSION_MINOR 9
|
||||
#define JSONCPP_VERSION_PATCH 3
|
||||
#define JSONCPP_VERSION_PATCH 5
|
||||
#define JSONCPP_VERSION_QUALIFIER
|
||||
#define JSONCPP_VERSION_HEXA \
|
||||
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
|
||||
@@ -162,11 +162,10 @@ public:
|
||||
* Release memory which was allocated for N items at pointer P.
|
||||
*
|
||||
* The memory block is filled with zeroes before being released.
|
||||
* The pointer argument is tagged as "volatile" to prevent the
|
||||
* compiler optimizing out this critical step.
|
||||
*/
|
||||
void deallocate(volatile pointer p, size_type n) {
|
||||
std::memset(p, 0, n * sizeof(T));
|
||||
void deallocate(pointer p, size_type n) {
|
||||
// memset_s is used because memset may be optimized away by the compiler
|
||||
memset_s(p, n * sizeof(T), 0, n * sizeof(T));
|
||||
// free using "global operator delete"
|
||||
::operator delete(p);
|
||||
}
|
||||
|
||||
@@ -6,28 +6,28 @@
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
The JsonCpp library's source code, including accompanying documentation,
|
||||
The JsonCpp library's source code, including accompanying documentation,
|
||||
tests and demonstration applications, are licensed under the following
|
||||
conditions...
|
||||
|
||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||
this software is released into the Public Domain.
|
||||
|
||||
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
|
||||
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
|
||||
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
|
||||
|
||||
In jurisdictions which recognize Public Domain property, the user of this
|
||||
software may choose to accept it either as 1) Public Domain, 2) under the
|
||||
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||
In jurisdictions which recognize Public Domain property, the user of this
|
||||
software may choose to accept it either as 1) Public Domain, 2) under the
|
||||
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||
Public Domain/MIT License conditions described here, as they choose.
|
||||
|
||||
The MIT License is about as close to Public Domain as a license can get, and is
|
||||
described in clear, concise terms at:
|
||||
|
||||
http://en.wikipedia.org/wiki/MIT_License
|
||||
|
||||
|
||||
The full text of the MIT License follows:
|
||||
|
||||
========================================================================
|
||||
@@ -93,10 +93,10 @@ license you like.
|
||||
// 3. /CMakeLists.txt
|
||||
// IMPORTANT: also update the SOVERSION!!
|
||||
|
||||
#define JSONCPP_VERSION_STRING "1.9.3"
|
||||
#define JSONCPP_VERSION_STRING "1.9.5"
|
||||
#define JSONCPP_VERSION_MAJOR 1
|
||||
#define JSONCPP_VERSION_MINOR 9
|
||||
#define JSONCPP_VERSION_PATCH 3
|
||||
#define JSONCPP_VERSION_PATCH 5
|
||||
#define JSONCPP_VERSION_QUALIFIER
|
||||
#define JSONCPP_VERSION_HEXA \
|
||||
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
|
||||
@@ -161,11 +161,10 @@ public:
|
||||
* Release memory which was allocated for N items at pointer P.
|
||||
*
|
||||
* The memory block is filled with zeroes before being released.
|
||||
* The pointer argument is tagged as "volatile" to prevent the
|
||||
* compiler optimizing out this critical step.
|
||||
*/
|
||||
void deallocate(volatile pointer p, size_type n) {
|
||||
std::memset(p, 0, n * sizeof(T));
|
||||
void deallocate(pointer p, size_type n) {
|
||||
// memset_s is used because memset may be optimized away by the compiler
|
||||
memset_s(p, n * sizeof(T), 0, n * sizeof(T));
|
||||
// free using "global operator delete"
|
||||
::operator delete(p);
|
||||
}
|
||||
@@ -575,7 +574,7 @@ public:
|
||||
// be used by...
|
||||
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4251)
|
||||
#pragma warning(disable : 4251 4275)
|
||||
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
|
||||
|
||||
#pragma pack(push, 8)
|
||||
@@ -788,10 +787,10 @@ private:
|
||||
CZString(ArrayIndex index);
|
||||
CZString(char const* str, unsigned length, DuplicationPolicy allocate);
|
||||
CZString(CZString const& other);
|
||||
CZString(CZString&& other);
|
||||
CZString(CZString&& other) noexcept;
|
||||
~CZString();
|
||||
CZString& operator=(const CZString& other);
|
||||
CZString& operator=(CZString&& other);
|
||||
CZString& operator=(CZString&& other) noexcept;
|
||||
|
||||
bool operator<(CZString const& other) const;
|
||||
bool operator==(CZString const& other) const;
|
||||
@@ -867,14 +866,15 @@ public:
|
||||
Value(const StaticString& value);
|
||||
Value(const String& value);
|
||||
Value(bool value);
|
||||
Value(std::nullptr_t ptr) = delete;
|
||||
Value(const Value& other);
|
||||
Value(Value&& other);
|
||||
Value(Value&& other) noexcept;
|
||||
~Value();
|
||||
|
||||
/// \note Overwrite existing comments. To preserve comments, use
|
||||
/// #swapPayload().
|
||||
Value& operator=(const Value& other);
|
||||
Value& operator=(Value&& other);
|
||||
Value& operator=(Value&& other) noexcept;
|
||||
|
||||
/// Swap everything.
|
||||
void swap(Value& other);
|
||||
@@ -1159,9 +1159,9 @@ private:
|
||||
public:
|
||||
Comments() = default;
|
||||
Comments(const Comments& that);
|
||||
Comments(Comments&& that);
|
||||
Comments(Comments&& that) noexcept;
|
||||
Comments& operator=(const Comments& that);
|
||||
Comments& operator=(Comments&& that);
|
||||
Comments& operator=(Comments&& that) noexcept;
|
||||
bool has(CommentPlacement slot) const;
|
||||
String get(CommentPlacement slot) const;
|
||||
void set(CommentPlacement slot, String comment);
|
||||
@@ -1442,8 +1442,8 @@ public:
|
||||
* because the returned references/pointers can be used
|
||||
* to change state of the base class.
|
||||
*/
|
||||
reference operator*() { return deref(); }
|
||||
pointer operator->() { return &deref(); }
|
||||
reference operator*() const { return const_cast<reference>(deref()); }
|
||||
pointer operator->() const { return const_cast<pointer>(&deref()); }
|
||||
};
|
||||
|
||||
inline void swap(Value& a, Value& b) { a.swap(b); }
|
||||
@@ -1506,8 +1506,7 @@ namespace Json {
|
||||
* \deprecated Use CharReader and CharReaderBuilder.
|
||||
*/
|
||||
|
||||
class JSONCPP_DEPRECATED(
|
||||
"Use CharReader and CharReaderBuilder instead.") JSON_API Reader {
|
||||
class JSON_API Reader {
|
||||
public:
|
||||
using Char = char;
|
||||
using Location = const Char*;
|
||||
@@ -1524,13 +1523,13 @@ public:
|
||||
};
|
||||
|
||||
/** \brief Constructs a Reader allowing all features for parsing.
|
||||
* \deprecated Use CharReader and CharReaderBuilder.
|
||||
*/
|
||||
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
|
||||
Reader();
|
||||
|
||||
/** \brief Constructs a Reader allowing the specified feature set for parsing.
|
||||
* \deprecated Use CharReader and CharReaderBuilder.
|
||||
*/
|
||||
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
|
||||
Reader(const Features& features);
|
||||
|
||||
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
|
||||
@@ -1797,6 +1796,9 @@ public:
|
||||
* - `"allowSpecialFloats": false or true`
|
||||
* - If true, special float values (NaNs and infinities) are allowed and
|
||||
* their values are lossfree restorable.
|
||||
* - `"skipBom": false or true`
|
||||
* - If true, if the input starts with the Unicode byte order mark (BOM),
|
||||
* it is skipped.
|
||||
*
|
||||
* You can examine 'settings_` yourself to see the defaults. You can also
|
||||
* write and read them just like any JSON Value.
|
||||
@@ -2000,6 +2002,8 @@ public:
|
||||
* - Number of precision digits for formatting of real values.
|
||||
* - "precisionType": "significant"(default) or "decimal"
|
||||
* - Type of precision for formatting of real values.
|
||||
* - "emitUTF8": false or true
|
||||
* - If true, outputs raw UTF8 strings instead of escaping them.
|
||||
|
||||
* You can examine 'settings_` yourself
|
||||
* to see the defaults. You can also write and read them just like any
|
||||
@@ -2035,7 +2039,7 @@ public:
|
||||
/** \brief Abstract class for writers.
|
||||
* \deprecated Use StreamWriter. (And really, this is an implementation detail.)
|
||||
*/
|
||||
class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer {
|
||||
class JSON_API Writer {
|
||||
public:
|
||||
virtual ~Writer();
|
||||
|
||||
@@ -2055,7 +2059,7 @@ public:
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996) // Deriving from deprecated class
|
||||
#endif
|
||||
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter
|
||||
class JSON_API FastWriter
|
||||
: public Writer {
|
||||
public:
|
||||
FastWriter();
|
||||
@@ -2115,7 +2119,7 @@ private:
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996) // Deriving from deprecated class
|
||||
#endif
|
||||
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
|
||||
class JSON_API
|
||||
StyledWriter : public Writer {
|
||||
public:
|
||||
StyledWriter();
|
||||
@@ -2184,7 +2188,7 @@ private:
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996) // Deriving from deprecated class
|
||||
#endif
|
||||
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
|
||||
class JSON_API
|
||||
StyledStreamWriter {
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -6,28 +6,28 @@
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
The JsonCpp library's source code, including accompanying documentation,
|
||||
The JsonCpp library's source code, including accompanying documentation,
|
||||
tests and demonstration applications, are licensed under the following
|
||||
conditions...
|
||||
|
||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
|
||||
jurisdictions which recognize such a disclaimer. In such jurisdictions,
|
||||
this software is released into the Public Domain.
|
||||
|
||||
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
|
||||
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
|
||||
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
|
||||
|
||||
In jurisdictions which recognize Public Domain property, the user of this
|
||||
software may choose to accept it either as 1) Public Domain, 2) under the
|
||||
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||
In jurisdictions which recognize Public Domain property, the user of this
|
||||
software may choose to accept it either as 1) Public Domain, 2) under the
|
||||
conditions of the MIT License (see below), or 3) under the terms of dual
|
||||
Public Domain/MIT License conditions described here, as they choose.
|
||||
|
||||
The MIT License is about as close to Public Domain as a license can get, and is
|
||||
described in clear, concise terms at:
|
||||
|
||||
http://en.wikipedia.org/wiki/MIT_License
|
||||
|
||||
|
||||
The full text of the MIT License follows:
|
||||
|
||||
========================================================================
|
||||
@@ -202,14 +202,18 @@ template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
|
||||
* Return iterator that would be the new end of the range [begin,end), if we
|
||||
* were to delete zeros in the end of string, but not the last zero before '.'.
|
||||
*/
|
||||
template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) {
|
||||
template <typename Iter>
|
||||
Iter fixZerosInTheEnd(Iter begin, Iter end, unsigned int precision) {
|
||||
for (; begin != end; --end) {
|
||||
if (*(end - 1) != '0') {
|
||||
return end;
|
||||
}
|
||||
// Don't delete the last zero before the decimal point.
|
||||
if (begin != (end - 1) && *(end - 2) == '.') {
|
||||
return end;
|
||||
if (begin != (end - 1) && begin != (end - 2) && *(end - 2) == '.') {
|
||||
if (precision) {
|
||||
return end;
|
||||
}
|
||||
return end - 2;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
@@ -338,8 +342,7 @@ bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
|
||||
|
||||
// Since String is reference-counted, this at least does not
|
||||
// create an extra copy.
|
||||
String doc;
|
||||
std::getline(is, doc, static_cast<char> EOF);
|
||||
String doc(std::istreambuf_iterator<char>(is), {});
|
||||
return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
|
||||
}
|
||||
|
||||
@@ -1409,8 +1412,11 @@ bool OurReader::readToken(Token& token) {
|
||||
if (features_.allowSingleQuotes_) {
|
||||
token.type_ = tokenString;
|
||||
ok = readStringSingleQuote();
|
||||
break;
|
||||
} // else fall through
|
||||
} else {
|
||||
// If we don't allow single quotes, this is a failure case.
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
token.type_ = tokenComment;
|
||||
ok = readComment();
|
||||
@@ -2152,7 +2158,7 @@ bool CharReaderBuilder::validate(Json::Value* invalid) const {
|
||||
if (valid_keys.count(key))
|
||||
continue;
|
||||
if (invalid)
|
||||
(*invalid)[std::move(key)] = *si;
|
||||
(*invalid)[key] = *si;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -2667,7 +2673,7 @@ Value::CZString::CZString(const CZString& other) {
|
||||
storage_.length_ = other.storage_.length_;
|
||||
}
|
||||
|
||||
Value::CZString::CZString(CZString&& other)
|
||||
Value::CZString::CZString(CZString&& other) noexcept
|
||||
: cstr_(other.cstr_), index_(other.index_) {
|
||||
other.cstr_ = nullptr;
|
||||
}
|
||||
@@ -2693,7 +2699,7 @@ Value::CZString& Value::CZString::operator=(const CZString& other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value::CZString& Value::CZString::operator=(CZString&& other) {
|
||||
Value::CZString& Value::CZString::operator=(CZString&& other) noexcept {
|
||||
cstr_ = other.cstr_;
|
||||
index_ = other.index_;
|
||||
other.cstr_ = nullptr;
|
||||
@@ -2841,7 +2847,7 @@ Value::Value(const Value& other) {
|
||||
dupMeta(other);
|
||||
}
|
||||
|
||||
Value::Value(Value&& other) {
|
||||
Value::Value(Value&& other) noexcept {
|
||||
initBasic(nullValue);
|
||||
swap(other);
|
||||
}
|
||||
@@ -2856,7 +2862,7 @@ Value& Value::operator=(const Value& other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator=(Value&& other) {
|
||||
Value& Value::operator=(Value&& other) noexcept {
|
||||
other.swap(*this);
|
||||
return *this;
|
||||
}
|
||||
@@ -3320,7 +3326,8 @@ void Value::resize(ArrayIndex newSize) {
|
||||
if (newSize == 0)
|
||||
clear();
|
||||
else if (newSize > oldSize)
|
||||
this->operator[](newSize - 1);
|
||||
for (ArrayIndex i = oldSize; i < newSize; ++i)
|
||||
(*this)[i];
|
||||
else {
|
||||
for (ArrayIndex index = newSize; index < oldSize; ++index) {
|
||||
value_.map_->erase(index);
|
||||
@@ -3781,14 +3788,15 @@ bool Value::isObject() const { return type() == objectValue; }
|
||||
Value::Comments::Comments(const Comments& that)
|
||||
: ptr_{cloneUnique(that.ptr_)} {}
|
||||
|
||||
Value::Comments::Comments(Comments&& that) : ptr_{std::move(that.ptr_)} {}
|
||||
Value::Comments::Comments(Comments&& that) noexcept
|
||||
: ptr_{std::move(that.ptr_)} {}
|
||||
|
||||
Value::Comments& Value::Comments::operator=(const Comments& that) {
|
||||
ptr_ = cloneUnique(that.ptr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value::Comments& Value::Comments::operator=(Comments&& that) {
|
||||
Value::Comments& Value::Comments::operator=(Comments&& that) noexcept {
|
||||
ptr_ = std::move(that.ptr_);
|
||||
return *this;
|
||||
}
|
||||
@@ -3804,13 +3812,11 @@ String Value::Comments::get(CommentPlacement slot) const {
|
||||
}
|
||||
|
||||
void Value::Comments::set(CommentPlacement slot, String comment) {
|
||||
if (!ptr_) {
|
||||
if (slot >= CommentPlacement::numberOfCommentPlacement)
|
||||
return;
|
||||
if (!ptr_)
|
||||
ptr_ = std::unique_ptr<Array>(new Array());
|
||||
}
|
||||
// check comments array boundry.
|
||||
if (slot < CommentPlacement::numberOfCommentPlacement) {
|
||||
(*ptr_)[slot] = std::move(comment);
|
||||
}
|
||||
(*ptr_)[slot] = std::move(comment);
|
||||
}
|
||||
|
||||
void Value::setComment(String comment, CommentPlacement placement) {
|
||||
@@ -4124,7 +4130,7 @@ Value& Path::make(Value& root) const {
|
||||
|
||||
#if !defined(isnan)
|
||||
// IEEE standard states that NaN values will not compare to themselves
|
||||
#define isnan(x) (x != x)
|
||||
#define isnan(x) ((x) != (x))
|
||||
#endif
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
@@ -4210,16 +4216,18 @@ String valueToString(double value, bool useSpecialFloats,
|
||||
|
||||
buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
|
||||
|
||||
// strip the zero padding from the right
|
||||
if (precisionType == PrecisionType::decimalPlaces) {
|
||||
buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end());
|
||||
}
|
||||
|
||||
// try to ensure we preserve the fact that this was given to us as a double on
|
||||
// input
|
||||
if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
|
||||
buffer += ".0";
|
||||
}
|
||||
|
||||
// strip the zero padding from the right
|
||||
if (precisionType == PrecisionType::decimalPlaces) {
|
||||
buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end(), precision),
|
||||
buffer.end());
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
} // namespace
|
||||
@@ -4231,11 +4239,11 @@ String valueToString(double value, unsigned int precision,
|
||||
|
||||
String valueToString(bool value) { return value ? "true" : "false"; }
|
||||
|
||||
static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
|
||||
static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
|
||||
assert(s || !n);
|
||||
|
||||
return std::any_of(s, s + n, [](unsigned char c) {
|
||||
return c == '\\' || c == '"' || !std::isprint(c);
|
||||
return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4326,12 +4334,12 @@ static void appendHex(String& result, unsigned ch) {
|
||||
result.append("\\u").append(toHex16Bit(ch));
|
||||
}
|
||||
|
||||
static String valueToQuotedStringN(const char* value, unsigned length,
|
||||
static String valueToQuotedStringN(const char* value, size_t length,
|
||||
bool emitUTF8 = false) {
|
||||
if (value == nullptr)
|
||||
return "";
|
||||
|
||||
if (!isAnyCharRequiredQuoting(value, length))
|
||||
if (!doesAnyCharRequireEscaping(value, length))
|
||||
return String("\"") + value + "\"";
|
||||
// We have to walk value and escape any special characters.
|
||||
// Appending to String is not efficient, but this should be rare.
|
||||
@@ -4404,7 +4412,7 @@ static String valueToQuotedStringN(const char* value, unsigned length,
|
||||
}
|
||||
|
||||
String valueToQuotedString(const char* value) {
|
||||
return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
|
||||
return valueToQuotedStringN(value, strlen(value));
|
||||
}
|
||||
|
||||
// Class Writer
|
||||
@@ -4453,7 +4461,7 @@ void FastWriter::writeValue(const Value& value) {
|
||||
char const* end;
|
||||
bool ok = value.getString(&str, &end);
|
||||
if (ok)
|
||||
document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str));
|
||||
document_ += valueToQuotedStringN(str, static_cast<size_t>(end - str));
|
||||
break;
|
||||
}
|
||||
case booleanValue:
|
||||
@@ -4476,8 +4484,7 @@ void FastWriter::writeValue(const Value& value) {
|
||||
const String& name = *it;
|
||||
if (it != members.begin())
|
||||
document_ += ',';
|
||||
document_ += valueToQuotedStringN(name.data(),
|
||||
static_cast<unsigned>(name.length()));
|
||||
document_ += valueToQuotedStringN(name.data(), name.length());
|
||||
document_ += yamlCompatibilityEnabled_ ? ": " : ":";
|
||||
writeValue(value[name]);
|
||||
}
|
||||
@@ -4522,7 +4529,7 @@ void StyledWriter::writeValue(const Value& value) {
|
||||
char const* end;
|
||||
bool ok = value.getString(&str, &end);
|
||||
if (ok)
|
||||
pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
|
||||
pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
|
||||
else
|
||||
pushValue("");
|
||||
break;
|
||||
@@ -4563,7 +4570,7 @@ void StyledWriter::writeValue(const Value& value) {
|
||||
}
|
||||
|
||||
void StyledWriter::writeArrayValue(const Value& value) {
|
||||
unsigned size = value.size();
|
||||
size_t size = value.size();
|
||||
if (size == 0)
|
||||
pushValue("[]");
|
||||
else {
|
||||
@@ -4572,7 +4579,7 @@ void StyledWriter::writeArrayValue(const Value& value) {
|
||||
writeWithIndent("[");
|
||||
indent();
|
||||
bool hasChildValue = !childValues_.empty();
|
||||
unsigned index = 0;
|
||||
ArrayIndex index = 0;
|
||||
for (;;) {
|
||||
const Value& childValue = value[index];
|
||||
writeCommentBeforeValue(childValue);
|
||||
@@ -4595,7 +4602,7 @@ void StyledWriter::writeArrayValue(const Value& value) {
|
||||
{
|
||||
assert(childValues_.size() == size);
|
||||
document_ += "[ ";
|
||||
for (unsigned index = 0; index < size; ++index) {
|
||||
for (size_t index = 0; index < size; ++index) {
|
||||
if (index > 0)
|
||||
document_ += ", ";
|
||||
document_ += childValues_[index];
|
||||
@@ -4740,7 +4747,7 @@ void StyledStreamWriter::writeValue(const Value& value) {
|
||||
char const* end;
|
||||
bool ok = value.getString(&str, &end);
|
||||
if (ok)
|
||||
pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
|
||||
pushValue(valueToQuotedStringN(str, static_cast<size_t>(end - str)));
|
||||
else
|
||||
pushValue("");
|
||||
break;
|
||||
@@ -5014,8 +5021,8 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
|
||||
char const* end;
|
||||
bool ok = value.getString(&str, &end);
|
||||
if (ok)
|
||||
pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str),
|
||||
emitUTF8_));
|
||||
pushValue(
|
||||
valueToQuotedStringN(str, static_cast<size_t>(end - str), emitUTF8_));
|
||||
else
|
||||
pushValue("");
|
||||
break;
|
||||
@@ -5038,8 +5045,8 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
|
||||
String const& name = *it;
|
||||
Value const& childValue = value[name];
|
||||
writeCommentBeforeValue(childValue);
|
||||
writeWithIndent(valueToQuotedStringN(
|
||||
name.data(), static_cast<unsigned>(name.length()), emitUTF8_));
|
||||
writeWithIndent(
|
||||
valueToQuotedStringN(name.data(), name.length(), emitUTF8_));
|
||||
*sout_ << colonSymbol_;
|
||||
writeValue(childValue);
|
||||
if (++it == members.end()) {
|
||||
@@ -5273,7 +5280,7 @@ bool StreamWriterBuilder::validate(Json::Value* invalid) const {
|
||||
if (valid_keys.count(key))
|
||||
continue;
|
||||
if (invalid)
|
||||
(*invalid)[std::move(key)] = *si;
|
||||
(*invalid)[key] = *si;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<!-- The packages.config acts as the global version for all of the NuGet packages contained within. -->
|
||||
<packages>
|
||||
<!-- Native packages -->
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
|
||||
@@ -59,7 +59,7 @@ To modify settings specific to the current application, invoke the `Properties`
|
||||
|
||||
When console applications are launched, the Windows Console Host determines which settings to use by overlaying settings from the above locations.
|
||||
|
||||
1. Initialize settings based on hardcoded defaults
|
||||
1. Initialize settings based on hard-coded defaults
|
||||
2. Overlay settings specified by the user's configured defaults
|
||||
3. Overlay application-specific settings from either the registry or the shortcut file, depending on how the application was launched
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ Our only backport successes really come from corporations with massive addressab
|
||||
|
||||
It's also costly in terms of time, effort, and testing for us to validate a modification to a released OS. We have a mindbogglingly massive amount of automated machinery dedicated to processing and validating the things that we check in while developing the current OS builds. But it's a special costly ask to spin up some to all of those activities to validate backported fixes. We do it all the time for Patch Tuesday, but in those patches, they only pass through the minimum number of fixes required to maximize the restoration of productivity/security/revenue/etc. because every additional fix adds additional complexity and additional risk.
|
||||
|
||||
So from our little team working hard to make developers happy, we virtually never make the cut for servicing. We're sorry, but we hope you can understand. It's just the reality of the situation to say "nope" when people ask for a backport. In our team's ideal world, you would all be running the latest console bits everywhere everytime we make a change. But that's just not how it is today.
|
||||
So from our little team working hard to make developers happy, we virtually never make the cut for servicing. We're sorry, but we hope you can understand. It's just the reality of the situation to say "nope" when people ask for a backport. In our team's ideal world, you would all be running the latest console bits everywhere every time we make a change. But that's just not how it is today.
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/279#issuecomment-439179675
|
||||
|
||||
|
||||
@@ -380,7 +380,7 @@ Here's the AppxManifest we're using:
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22621.0" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
</Dependencies>
|
||||
@@ -517,7 +517,7 @@ This is because of a few key lines we already put in the appxmanifest:
|
||||
|
||||
```xml
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22621.0" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
</Dependencies>
|
||||
|
||||
@@ -82,7 +82,14 @@
|
||||
"properties": {
|
||||
"colorScheme": {
|
||||
"description": "The name of a color scheme to use when unfocused.",
|
||||
"type": "string"
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/SchemePair"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"foreground": {
|
||||
"$ref": "#/$defs/Color",
|
||||
@@ -203,7 +210,7 @@
|
||||
},
|
||||
"intenseTextStyle": {
|
||||
"default": "bright",
|
||||
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
|
||||
"description": "Controls how 'intense' text is rendered when unfocused. Values are \"bold\", \"bright\", \"all\" and \"none\"",
|
||||
"enum": [
|
||||
"none",
|
||||
"bold",
|
||||
@@ -233,6 +240,21 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"SchemePair": {
|
||||
"description": "Contains both a light and dark color scheme for the Terminal to use, depending on the theme of the application.",
|
||||
"properties": {
|
||||
"light": {
|
||||
"default": "Campbell",
|
||||
"description": "Name of the scheme to use when the app is using light theme",
|
||||
"type": "string"
|
||||
},
|
||||
"dark": {
|
||||
"default": "Campbell",
|
||||
"description": "Name of the scheme to use when the app is using dark theme",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"FontConfig": {
|
||||
"properties": {
|
||||
"face": {
|
||||
@@ -244,7 +266,7 @@
|
||||
"default": 12,
|
||||
"description": "Size of the font in points.",
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
"type": "number"
|
||||
},
|
||||
"weight": {
|
||||
"default": "normal",
|
||||
@@ -539,6 +561,160 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"NewTabMenuEntryType": {
|
||||
"enum": [
|
||||
"source",
|
||||
"profile",
|
||||
"folder",
|
||||
"separator",
|
||||
"remainingProfiles",
|
||||
"matchProfiles"
|
||||
]
|
||||
},
|
||||
"NewTabMenuEntry": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"description": "The type of menu entry",
|
||||
"$ref": "#/$defs/NewTabMenuEntryType"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"FolderEntryInlining": {
|
||||
"enum": [
|
||||
"never",
|
||||
"auto"
|
||||
]
|
||||
},
|
||||
"FolderEntry": {
|
||||
"description": "A folder entry in the new tab dropdown",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NewTabMenuEntry"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "folder"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The name of the folder to show in the menu"
|
||||
},
|
||||
"icon": {
|
||||
"$ref": "#/$defs/Icon"
|
||||
},
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"description": "The entries to put inside this folder",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/$defs/NewTabMenuEntry"
|
||||
}
|
||||
},
|
||||
"inline": {
|
||||
"description": "When set to auto and the folder only has a single entry, the entry will show directly and no folder will be rendered",
|
||||
"default": "never",
|
||||
"$ref": "#/$defs/FolderEntryInlining"
|
||||
},
|
||||
"allowEmpty": {
|
||||
"description": "Whether to render a folder without entries, or to hide it",
|
||||
"default": "false",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"SeparatorEntry": {
|
||||
"description": "A separator in the new tab dropdown",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NewTabMenuEntry"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "separator"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ProfileEntry": {
|
||||
"description": "A profile in the new tab dropdown",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NewTabMenuEntry"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "profile"
|
||||
},
|
||||
"profile": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The name or GUID of the profile to show in this entry"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"RemainingProfilesEntry": {
|
||||
"description": "The set of profiles that are not yet explicitly included in another entry, such as the profile or source entries. This entry can be used at most one time!",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NewTabMenuEntry"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "remainingProfiles"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"MatchProfilesEntry": {
|
||||
"description": "A set of profiles all matching the given name, source, or command line, to show in the new tab dropdown",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NewTabMenuEntry"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "matchProfiles"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The name of the profiles to match"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The source of the profiles to match"
|
||||
},
|
||||
"commandline": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The command line of the profiles to match"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"SwitchToAdjacentTabArgs": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -1546,6 +1722,145 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ShowCloseButton": {
|
||||
"enum": [
|
||||
"always",
|
||||
"hover",
|
||||
"never"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThemeColor": {
|
||||
"description": "A special kind of color for use in themes. Can be an #rrggbb color, #rrggbbaa color, or a special value. 'accent' is evaluated as the user's selected Accent color in the OS, and 'terminalBackground' will be evaluated as the background color of the active terminal pane.",
|
||||
"oneOf": [
|
||||
{
|
||||
"pattern": "^#[A-Fa-f0-9]{3}(?:[A-Fa-f0-9]{3}(?:[A-Fa-f0-9]{2})?)?$",
|
||||
"type": "string",
|
||||
"format": "color",
|
||||
"default": "#000000ff"
|
||||
},
|
||||
{
|
||||
"const": "accent",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"const": "terminalBackground",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"TabTheme": {
|
||||
"additionalProperties": false,
|
||||
"description": "A set of properties for customizing the appearance of the tabs",
|
||||
"properties": {
|
||||
"background": {
|
||||
"description": "The color of a tab when it is the active tab",
|
||||
"$ref": "#/$defs/ThemeColor"
|
||||
},
|
||||
"unfocusedBackground": {
|
||||
"description": "The color of a tab when it is not the active tab",
|
||||
"$ref": "#/$defs/ThemeColor"
|
||||
},
|
||||
"showCloseButton": {
|
||||
"description": "Controls the visibility of the close button on the tab",
|
||||
"$ref": "#/$defs/ShowCloseButton"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TabRowTheme": {
|
||||
"additionalProperties": false,
|
||||
"description": "A set of properties for customizing the appearance of the tab row",
|
||||
"properties": {
|
||||
"background": {
|
||||
"description": "The color of the tab row when the window is the foreground window.",
|
||||
"$ref": "#/$defs/ThemeColor"
|
||||
},
|
||||
"unfocusedBackground": {
|
||||
"description": "The color of the tab row when the window is inactive",
|
||||
"$ref": "#/$defs/ThemeColor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"WindowTheme": {
|
||||
"additionalProperties": false,
|
||||
"description": "A set of properties for customizing the appearance of the window itself",
|
||||
"properties": {
|
||||
"applicationTheme": {
|
||||
"description": "Which UI theme the Terminal should use for controls",
|
||||
"enum": [ "light", "dark", "system" ],
|
||||
"type": "string"
|
||||
},
|
||||
"useMica": {
|
||||
"description": "True if the Terminal should use a Mica backdrop for the window. This will apply underneath all controls (including the terminal panes and the titlebar)",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"Theme": {
|
||||
"additionalProperties": false,
|
||||
"description": "A set of properties for customizing the appearance of the window. This controls things like the titlebar, the tabs, the application theme.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the theme. This will be displayed in the settings UI.",
|
||||
"not": {
|
||||
"enum": [ "light", "dark", "system" ]
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"$ref": "#/$defs/TabTheme"
|
||||
},
|
||||
"tabRow": {
|
||||
"$ref": "#/$defs/TabRowTheme"
|
||||
},
|
||||
"window": {
|
||||
"$ref": "#/$defs/WindowTheme"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ThemePair": {
|
||||
"additionalProperties": false,
|
||||
"description": "A pair of Theme names, to allow the Terminal to switch theme based on the OS theme",
|
||||
"properties": {
|
||||
"light": {
|
||||
"type": "string",
|
||||
"description": "The name of the theme to use when the OS is in Light theme",
|
||||
"default": "light"
|
||||
},
|
||||
"dark": {
|
||||
"type": "string",
|
||||
"description": "The name of the theme to use when the OS is in Dark theme",
|
||||
"default": "dark"
|
||||
}
|
||||
}
|
||||
},
|
||||
"NewTabMenu": {
|
||||
"description": "Defines the order and structure of the 'new tab' menu. It can consist of e.g. profiles, folders, and separators.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/FolderEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/SeparatorEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/ProfileEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/MatchProfilesEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/RemainingProfilesEntry"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -1924,20 +2239,32 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"newTabMenu": {
|
||||
"$ref": "#/$defs/NewTabMenu"
|
||||
},
|
||||
"language": {
|
||||
"default": "",
|
||||
"description": "Sets an override for the app's preferred language, expressed as a BCP-47 language tag like en-US.",
|
||||
"type": "string"
|
||||
},
|
||||
"theme": {
|
||||
"default": "system",
|
||||
"description": "Sets the theme of the application. The special value \"system\" refers to the active Windows system theme.",
|
||||
"enum": [
|
||||
"light",
|
||||
"dark",
|
||||
"system"
|
||||
],
|
||||
"type": "string"
|
||||
"default": "dark",
|
||||
"description": "Sets the theme of the application. This value should be the name of one of the themes defined in `themes`. The Terminal also includes the themes `dark`, `light`, and `system`.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/ThemePair"
|
||||
}
|
||||
]
|
||||
},
|
||||
"themes": {
|
||||
"description": "The list of available themes",
|
||||
"items": {
|
||||
"$ref": "#/$defs/Theme"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"showTabsInTitlebar": {
|
||||
"default": true,
|
||||
@@ -2170,7 +2497,14 @@
|
||||
"colorScheme": {
|
||||
"default": "Campbell",
|
||||
"description": "Name of the terminal color scheme to use. Color schemes are defined under \"schemes\".",
|
||||
"type": "string"
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/SchemePair"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"commandline": {
|
||||
"description": "Executable used in the profile.",
|
||||
@@ -2252,7 +2586,7 @@
|
||||
"default": 12,
|
||||
"description": "[deprecated] Define 'size' within the 'font' object instead.",
|
||||
"minimum": 1,
|
||||
"type": "integer",
|
||||
"type": "number",
|
||||
"deprecated": true
|
||||
},
|
||||
"fontWeight": {
|
||||
@@ -2283,6 +2617,17 @@
|
||||
],
|
||||
"deprecated": true
|
||||
},
|
||||
"intenseTextStyle": {
|
||||
"default": "bright",
|
||||
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
|
||||
"enum": [
|
||||
"none",
|
||||
"bold",
|
||||
"bright",
|
||||
"all"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"foreground": {
|
||||
"$ref": "#/$defs/Color",
|
||||
"default": "#cccccc",
|
||||
@@ -2350,7 +2695,8 @@
|
||||
"description": "Defines the visibility of the scrollbar.",
|
||||
"enum": [
|
||||
"visible",
|
||||
"hidden"
|
||||
"hidden",
|
||||
"always"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2384,7 +2730,10 @@
|
||||
},
|
||||
"startingDirectory": {
|
||||
"description": "The directory the shell starts in when it is loaded.",
|
||||
"type": "string"
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"suppressApplicationTitle": {
|
||||
"description": "When set to true, tabTitle overrides the default title of the tab and any title change messages from the application will be suppressed. When set to false, tabTitle behaves as normal.",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2020-5-13
|
||||
last updated: 2020-08-04
|
||||
last updated: 2022-11-18
|
||||
issue id: 1571
|
||||
---
|
||||
|
||||
@@ -76,6 +76,34 @@ There are five `type`s of objects in this menu:
|
||||
- The `"entries"` property specifies a list of menu entries that will appear
|
||||
nested under this entry. This can contain other `"type":"folder"` groups as
|
||||
well!
|
||||
- The `"inline"` property accepts two values
|
||||
- `auto`: When the folder only has one entry in it, don't actually create a
|
||||
nested layer to then menu. Just place the single entry in the layer that
|
||||
folder would occupy. (Useful for dynamic profile sources with only a
|
||||
single entry).
|
||||
- `never`: (**default**) Always create a nested entry, even for a single
|
||||
sub-item.
|
||||
- The `allowEmpty` property will force this entry to show up in the menu, even
|
||||
if it doesn't have any profiles in it. This defaults to `false`, meaning
|
||||
that folders without any entries in them will just be ignored when
|
||||
generating the menu. This will be more useful with the `matchProfile` entry,
|
||||
below.
|
||||
|
||||
When this is true, and the folder is empty, we should add a
|
||||
placeholder `<empty>` entry to the menu, to indicate that no profiles were
|
||||
in that folder.
|
||||
- _This setting is probably pretty niche, and not a requirement_. More of a
|
||||
theoretical suggestion than anything.
|
||||
- In the case of no entries for this folder, we should make sure to also
|
||||
reflect the `inline` property:
|
||||
- `allowEmpty:true`, `inline:auto`: just ignore the entry at all. Don't
|
||||
add a placeholder to the parent list.
|
||||
- `allowEmpty:true`, `inline:never`: Add a nested entry, with an
|
||||
`<empty>` placeholder.
|
||||
- `allowEmpty:false`, `inline:auto`: just ignore the entry at all. Don't
|
||||
add a placeholder to the parent list.
|
||||
- `allowEmpty:false`, `inline:never`: just ignore the entry at all. Don't
|
||||
add a placeholder to the parent list.
|
||||
* `"type":"action"`: This represents a menu entry that should execute a specific
|
||||
`ShortcutAction`.
|
||||
- the `id` property will specify the global action ID (see [#6899], [#7175])
|
||||
@@ -97,6 +125,16 @@ There are five `type`s of objects in this menu:
|
||||
enabling all other profiles to also be accessible.
|
||||
- The "name" of these entries will simply be the name of the profile
|
||||
- The "icon" of these entries will simply be the profile's icon
|
||||
- This won't include any profiles that have been included via `matchProfile`
|
||||
entries (below)
|
||||
* `"type": "matchProfile"`: Expands to all the profiles that match a given
|
||||
string. This lets the user easily specify a whole collection of profiles for a
|
||||
folder, without needing to add them all manually.
|
||||
- `"name"`, `"commandline"` or `"source"`: These three properties are used to
|
||||
filter the list of profiles, based on the matching property in the profile
|
||||
itself. The value is a string to compare with the corresponding property in
|
||||
the profile. A full string comparison is done - not a regex or partial
|
||||
string match.
|
||||
|
||||
The "default" new tab menu could be imagined as the following blob of json:
|
||||
|
||||
@@ -108,6 +146,42 @@ The "default" new tab menu could be imagined as the following blob of json:
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, we could consider something like the following. This would place
|
||||
CMD, PowerShell, and all PowerShell cores in the root at the top, followed by
|
||||
nested entries for each subsequent dynamic profile generator.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"newTabMenu": [
|
||||
{ "type":"profile", "profile": "cmd" },
|
||||
{ "type":"profile", "profile": "Windows PowerShell" },
|
||||
{ "type": "matchProfile", "source": "Microsoft.Terminal.PowerShellCore" }
|
||||
{
|
||||
"type": "folder",
|
||||
"name": "WSL",
|
||||
"entries": [ { "type": "matchProfile", "source": "Microsoft.Terminal.Wsl" } ]
|
||||
},
|
||||
{
|
||||
"type": "folder",
|
||||
"name": "Visual Studio",
|
||||
"entries": [ { "type": "matchProfile", "source": "Microsoft.Terminal.VisualStudio" } ]
|
||||
},
|
||||
// ... etc for other profile generators
|
||||
{ "type": "remainingProfiles" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
I might only recommend that for `userDefaults.json`, which is the json files
|
||||
used as a template for a user's new settings file. This would prevent us from
|
||||
moving the user's cheese too much, if they're already using the Terminal and
|
||||
happy with their list as is. Especially consider someone who's default profile
|
||||
is a WSL distro, which would now need two clicks to get to.
|
||||
|
||||
> _note_: We will also want to support the same `{ "key": "SomeResourceString"}`
|
||||
> syntax used by the Command Palette commands
|
||||
> for specifying localizable names, if we chose to pursue this route.
|
||||
|
||||
### Other considerations
|
||||
|
||||
Also considered during the investigation for this feature was re-using the list
|
||||
@@ -154,6 +228,42 @@ The design chosen in this spec more cleanly separates the responsibilities of
|
||||
the list of profiles and the contents of the new tab menu. This way, each object
|
||||
can be defined independent of the structure of the other.
|
||||
|
||||
Regarding implementation of `matchProfile` entries: In order to build the menu,
|
||||
we'll evaluate the entries in the following order:
|
||||
|
||||
* all explicit `profile` entries
|
||||
* then all `matchProfile` entries, using profiles not already specified
|
||||
* then expand out `remainingProfiles` with anything not found above.
|
||||
|
||||
As an example:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"newTabMenu": [
|
||||
{ "type": "matchProfile", "source": "Microsoft.Terminal.Wsl" }
|
||||
{
|
||||
"type": "folder",
|
||||
"name": "WSLs",
|
||||
"entries": [ { "type": "matchProfile", "source": "Microsoft.Terminal.Wsl" } ]
|
||||
},
|
||||
{ "type": "remainingProfiles" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
For profiles { "Profile A", "Profile B (WSL)", "Profile C (WSL)" }, This would
|
||||
expand to:
|
||||
|
||||
```
|
||||
New Tab Button ▽
|
||||
├─ Profile A
|
||||
├─ Profile B (WSL)
|
||||
├─ Profile C (WSL)
|
||||
└─ WSLs
|
||||
└─ Profile B (WSL)
|
||||
└─ Profile C (WSL)
|
||||
```
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
See the above _figure 1_.
|
||||
@@ -289,7 +399,39 @@ And assuming the user has bound:
|
||||
- Close Tab: `{ "action": "closeTab", "index": "${selectedTab.index}" }`
|
||||
- Close Other Tabs: `{ "action": "closeTabs", "otherThan": "${selectedTab.index}" }`
|
||||
- Close Tabs to the Right: `{ "action": "closeTabs", "after": "${selectedTab.index}" }`
|
||||
* We may want to consider regex, tag-based, or some other type of matching for
|
||||
`matchProfile` entries in the future. We originally considered using regex for
|
||||
`matchProfile` by default, but decided instead on full string matches to leave
|
||||
room for regex matching in the future. Should we chose to pursue something
|
||||
like that, we should use a settings structure like:
|
||||
|
||||
```json
|
||||
"type": "profileMatch",
|
||||
"source": { "type": "regex", "value": ".*wsl.*" }
|
||||
```
|
||||
* We may want to expand `matchProfile` to match on other properties too. (`title`?)
|
||||
* We may want to consider adding support for capture groups, e.g.
|
||||
```json
|
||||
{
|
||||
"type": "profileMatch",
|
||||
"name": { "type": "regex", "value": "^ssh: (.*)" }
|
||||
}
|
||||
```
|
||||
for matching to all your `ssh: ` profiles, but populate the name in the entry
|
||||
with that first capture group. So, ["ssh: foo", "ssh: bar"] would just expand
|
||||
to a "foo" and "bar" entry.
|
||||
|
||||
## Updates
|
||||
|
||||
_February 2022_: Doc updated in response to some discussion in [#11326] and
|
||||
[#7774]. In those PRs, it became clear that there needs to be a simple way of
|
||||
collecting up a whole group of profiles automatically for sorting in these
|
||||
menus. Although discussion centered on how hard it would be for extensions to
|
||||
provide that customization themselves, the `match` statement was added as a way
|
||||
to allow the user to easily filter those profiles themselves.
|
||||
|
||||
This was something we had originally considered as a "future consideration", but
|
||||
ultimately deemed it to be out of scope for the initial spec review.
|
||||
|
||||
<!-- Footnotes -->
|
||||
[#2046]: https://github.com/microsoft/terminal/issues/2046
|
||||
@@ -298,3 +440,5 @@ And assuming the user has bound:
|
||||
[#3337]: https://github.com/microsoft/terminal/issues/3337
|
||||
[#6899]: https://github.com/microsoft/terminal/issues/6899
|
||||
[#7175]: https://github.com/microsoft/terminal/issues/7175
|
||||
[#11326]: https://github.com/microsoft/terminal/issues/11326
|
||||
[#7774]: https://github.com/microsoft/terminal/issues/7774
|
||||
|
||||
@@ -201,7 +201,7 @@ Concerns:
|
||||
|
||||
### Accessibility
|
||||
|
||||
Accessibility applications are the most likely to resort to a method of spelunking the process tree or window handles to attempt to find content to read out. Presuming they have hardcoded rules for console-type applications, these algorithms could be surprised by the substitution of another terminal environment.
|
||||
Accessibility applications are the most likely to resort to a method of spelunking the process tree or window handles to attempt to find content to read out. Presuming they have hard-coded rules for console-type applications, these algorithms could be surprised by the substitution of another terminal environment.
|
||||
|
||||
The major players here that I am considering are NVDA, JAWS, and Narrator. As far as I am aware, all of these applications attempt to drive their interactivity through UI Automation where possible. And we have worked with all of these applications in the past in improving their support for both `conhost.exe` and the Windows Terminal product. I have relatively high confidence that we will be able to work with them again to help update these assistive products to understand the new UI delegation, if necessary.
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ These are in accordance with ConHost's keyboard selection model.
|
||||
|
||||
This idea was abandoned due to several reasons:
|
||||
1. Keyboard selection should be a standard way to interact with a terminal across all consumers (i.e. WPF control, etc.)
|
||||
2. There isn't really another set of key bindings that makes sense for this. We already hardcoded <kbd>ESC</kbd> as a way to clear the selection. This is just an extension of that.
|
||||
2. There isn't really another set of key bindings that makes sense for this. We already hard-coded <kbd>ESC</kbd> as a way to clear the selection. This is just an extension of that.
|
||||
3. Adding 12 conditionally effective key bindings takes the spot of 12 potential non-conditional key bindings. It would be nice if a different key binding could be set when the selection is not active, but that makes the settings design much more complicated.
|
||||
4. 12 new items in the command palette is also pretty excessive.
|
||||
5. If proven wrong when this is in WT Preview, we can revisit this and make them customizable then. It's better to add the ability to customize it later than take it away.
|
||||
|
||||
@@ -115,7 +115,7 @@ greater detail below:
|
||||
|
||||
### Default Settings
|
||||
|
||||
We'll have a static version of the "Default" file **hardcoded within the
|
||||
We'll have a static version of the "Default" file **hard-coded within the
|
||||
application package**. This `defaults.json` file will live within the
|
||||
application's package, which will prevent users from being able to edit it.
|
||||
|
||||
@@ -128,19 +128,19 @@ won't actually be generated, but because it's shipped with our app, it'll be
|
||||
overridden each time the app is updated. "Auto-generated" should be good enough
|
||||
to indicate to users that it should not be modified.
|
||||
|
||||
Because the `defaults.json` file is hardcoded within our application, we can use
|
||||
Because the `defaults.json` file is hard-coded within our application, we can use
|
||||
its text directly, without loading the file from disk. This should help save
|
||||
some startup time, as we'll only need to load the user settings from disk.
|
||||
|
||||
When we make changes to the default settings, or we make changes to the settings
|
||||
schema, we should make sure that we update the hardcoded `defaults.json` with
|
||||
schema, we should make sure that we update the hard-coded `defaults.json` with
|
||||
the new values. That way, the `defaults.json` file will always have the complete
|
||||
set of settings in it.
|
||||
|
||||
### Layering settings
|
||||
|
||||
When we load the settings, we'll do it in three stages. First, we'll deserialize
|
||||
the default settings that we've hardcoded. We'll then generate any profiles that
|
||||
the default settings that we've hard-coded. We'll then generate any profiles that
|
||||
might come from dynamic profile sources. Then, we'll intelligently layer the
|
||||
user's setting upon those we've already loaded. If a user wants to make changes
|
||||
to some objects, like the default profiles, we'll need to make sure to load from
|
||||
|
||||
@@ -158,7 +158,7 @@ For `settings.json`, `_globals` will only hold the values set in `settings.json`
|
||||
This process becomes a bit more complex for `Profile` because it can fallback in the following order:
|
||||
1. `settings.json` profile
|
||||
2. `settings.json` `profiles.defaults`
|
||||
3. (if a dynamic profile) the hardcoded value in the dynamic profile generator
|
||||
3. (if a dynamic profile) the hard-coded value in the dynamic profile generator
|
||||
4. `defaults.json` profile
|
||||
|
||||
`CascadiaSettings` must do the following...
|
||||
@@ -280,7 +280,7 @@ TerminalApp will construct and reference a `CascadiaSettings settings` as follow
|
||||
and layers the settings.json data on top of it.
|
||||
- check for errors/warnings, and handle them appropriately
|
||||
|
||||
This will be different from the current model which has the settings.json path hardcoded, and is simplified
|
||||
This will be different from the current model which has the settings.json path hard-coded, and is simplified
|
||||
to a `LoadAll()` call wrapped in error handlers.
|
||||
|
||||
**NOTE:** This model allows us to layer even more settings files on top of the existing Terminal Settings
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2021-03-03
|
||||
last updated: 2022-11-04
|
||||
issue id: #2634
|
||||
---
|
||||
|
||||
# Broadcast Input
|
||||
|
||||
## Abstract
|
||||
|
||||
"Broadcast Input" is a feature present on other terminals which allows the user
|
||||
to send the same input to multiple tabs or panes at the same time. This can make
|
||||
it simpler for the user to run the same command in multiple directories or
|
||||
servers at the same time.
|
||||
|
||||
With a viable prototype in [#9222], it's important that we have a well-defined
|
||||
plan for how we want this feature to be exposed before merging that PR. This
|
||||
spec is intended to be a lighter-than-usual spec to build consensus on the
|
||||
design of how the actions should be expressed.
|
||||
|
||||
## Background
|
||||
|
||||
### Inspiration
|
||||
|
||||
This spec is heavily inspired by the [iTerm2 implementation]. @carlos-zamora did
|
||||
a great job of breaking down how iTerm2 works in [this comment].
|
||||
|
||||
SecureCRT also implements a similar feature using a "chat window" that can send
|
||||
the input in the chat window to all tabs. This seemed like a less ergonomic
|
||||
solution, so it was not seriously considered.
|
||||
|
||||
Additionally, Terminator (on \*nix) allows for a similar feature through the use
|
||||
of "groups". From [@zljubisic]:
|
||||
|
||||
> In Linux terminator you can define groups, and than put each pane in one of
|
||||
> defined groups. Afterwards, you can choose broadcasting to all panes, only
|
||||
> certain group or no broadcast at all.
|
||||
|
||||
This also seemed like a less powerful version of broadcast input than the
|
||||
iterm2-like version, so it was also not further investigated.
|
||||
|
||||
### User Stories
|
||||
|
||||
iTerm2 supports the following actions:
|
||||
|
||||
* **Story A:** _Send input to current session only_: The default setting.
|
||||
* **Story B:** _Broadcast to all panes in all tabs_: Anything you type on the
|
||||
keyboard goes to all sessions in this window.
|
||||
* **Story C:** _Broadcast to all panes in current tab_: Anything you type on the
|
||||
keyboard goes to all sessions in this tab.
|
||||
* **Story D:** _Toggle broadcast input to current session_: Toggles whether this
|
||||
session receives broadcasted keystrokes within this window.
|
||||
|
||||
|
||||
## Solution Design
|
||||
|
||||
### Proposal 1: iTerm2-like Modal Input Broadcast
|
||||
|
||||
iTerm2 implements broadcast input as a type of "modal" system. The user is in
|
||||
one of the following modes:
|
||||
|
||||
* Broadcast to all panes in all tabs
|
||||
* Broadcast to all panes in the current tab
|
||||
* Broadcast to some set of panes within the current tab
|
||||
* Don't broadcast input at all (the default behavior)
|
||||
|
||||
These modes are vaguely per-tab state. There's a global "broadcast to all tabs &
|
||||
panes" property. Then, each tab also has a pair of values:
|
||||
* Should input be sent to all panes in this tab?
|
||||
* If not, which panes should input be sent to?
|
||||
|
||||
It's not possible to send input to one pane in tab A, then another pane in tab
|
||||
B, without enabling the global "broadcast to everyone" mode.
|
||||
|
||||
This seems to break down into the following actions:
|
||||
|
||||
```json
|
||||
{ "action": "toggleBroadcastInput", "scope": "window" },
|
||||
{ "action": "toggleBroadcastInput", "scope": "tab" },
|
||||
{ "action": "toggleBroadcastInput", "scope": "pane" },
|
||||
{ "action": "disableBroadcastInput" },
|
||||
```
|
||||
|
||||
Which would be accompanied by the following internal properties:
|
||||
* A window (`TerminalPage`-level) property for `broadcastToAllPanesAndTabs`
|
||||
* A per-tab property for `broadcastToAllPanes`
|
||||
* A per-tab set of panes to broadcast to
|
||||
|
||||
The scopes would work as follows:
|
||||
|
||||
* `"scope": "window"`: Toggle the window's "broadcast to all tabs and panes"
|
||||
setting.
|
||||
* `"scope": "tab"`: Toggle the tab's "broadcast to all panes in this tab"
|
||||
setting.
|
||||
- This does not modify the set of panes that the user is broadcasting to in
|
||||
the tab, merely toggles the tab's setting. If the user has a set of panes
|
||||
they're broadcasting to in this tab, then toggles this setting on and off,
|
||||
we'll return to broadcasting to that set.
|
||||
* `"scope": "pane"`: Add this pane to the set of panes being broadcasted to in
|
||||
this tab.
|
||||
- **TODO!: FOR DISCUSSION**: Should this disable the tab's
|
||||
"broadcastToAllPanes" setting? Or should it leave that alone?
|
||||
* `"disableBroadcastInput"`: Set the global setting to false, the tab's setting
|
||||
to false, and clear the set of panes being broadcasted to for this tab.
|
||||
- **TODO!** This could also just be `"action": "toggleBroadcastInput",
|
||||
"scope": "none"`
|
||||
|
||||
#### Pros
|
||||
* This is exactly how iTerm2 does it, so there's prior art.
|
||||
* If you're not globally broadcasting, then you're only ever broadcasting to
|
||||
some (sub)set of the panes in the current tab. So global broadcast mode is
|
||||
the only time a user would need to worry about input being to be sent to
|
||||
an inactive tab.
|
||||
* You can have a set of panes to broadcast to in the first tab, then a
|
||||
_separate_ set to broadcast to in a second tab. Broadcasting in one tab
|
||||
does not affect the other.
|
||||
|
||||
#### Cons
|
||||
* I frankly think the `tab`/`pane` interaction can be a little weird. Like for
|
||||
this scenario:
|
||||
- enable broadcast input for tab 1
|
||||
- switch to tab 2
|
||||
- enable broadcast input for a pane in tab 2
|
||||
|
||||
There's valid confusion to be had between the following two behaviors:
|
||||
1. input goes to all of tab 1 _and_ the pane in tab 2
|
||||
2. input only goes to the pane in tab 2
|
||||
* You can't broadcast to a subset of panes in inactive tabs, in addition to
|
||||
the active tab. All panes you want to broadcast to must be in the active
|
||||
tab.
|
||||
* Does creating a new split in a pane that's being broadcast to add that pane to
|
||||
the broadcast set?
|
||||
|
||||
#### What would this mean for PR #9222?
|
||||
|
||||
The prototype PR [#9222] basically just implemented `{ "action":
|
||||
"toggleBroadcastInput", "scope": "tab" }`. We could make `tab` the default
|
||||
`scope` if no other one is specified, and then the PR would need basically no
|
||||
modifications. Future PRs could add args to the `toggleBroadcastInput` action,
|
||||
without breaking users who bind a key to that action now.
|
||||
|
||||
### Proposal 2: Broadcast Set
|
||||
|
||||
This was the design I had originally came up with before investigating iTerm2
|
||||
much closer. This design involves a "broadcast set" of panes. All the panes in
|
||||
the broadcast set would also get the `KeySent` and `CharSent` events, in
|
||||
addition to the active pane. (The active pane may be a part of the broadcast
|
||||
set). If a pane is read-only in the broadcast set, then it won't handle those
|
||||
broadcasted events (obviously).
|
||||
|
||||
As far as actions, we're looking at something like:
|
||||
|
||||
* **A** Only send input to the active pane
|
||||
* Remove all the panes from the broadcast set
|
||||
* **B** send input to all panes in all tabs
|
||||
* If all the panes are in the broadcast set, remove them all. Otherwise, add
|
||||
all panes in all tabs to the broadcast set.
|
||||
* **C** send input to all panes in the current tab
|
||||
* If all the panes in the current tab are in the broadcast set, remove them
|
||||
from the broadcast set. Otherwise, add all the panes from this tab to the
|
||||
broadcast set.
|
||||
* **D** toggle sending input to the current pane
|
||||
* If this pane is in the broadcast set, remove it. Otherwise add it.
|
||||
This seems to break down into the following actions:
|
||||
|
||||
```json
|
||||
{ "action": "disableBroadcastInput" },
|
||||
{ "action": "toggleBroadcastInput", "scope": "window" },
|
||||
{ "action": "toggleBroadcastInput", "scope": "tab" },
|
||||
{ "action": "toggleBroadcastInput", "scope": "pane" },
|
||||
```
|
||||
|
||||
Which would be accompanied by the following internal properties:
|
||||
* A window (`TerminalPage`-level) set of panes to broadcast to.
|
||||
|
||||
#### Pros:
|
||||
* Mentally, you're either adding panes to the set of panes to broadcast to, or
|
||||
removing them.
|
||||
* You can broadcast to panes in multiple tabs, without broadcasting to _all_
|
||||
panes in all tabs.
|
||||
|
||||
#### Cons:
|
||||
* is _slightly_ different from iTerm2.
|
||||
* Does creating a new split in a pane that's being broadcast to add that pane to
|
||||
the broadcast set?
|
||||
* You can't have a set of panes to broadcast to in the one tab, and a different
|
||||
set in another tab. As an example:
|
||||
1. in tab 1, you add panes A and B to the broadcast set. Typing in either one
|
||||
goes to both A and B.
|
||||
2. in tab 1, switch to pane C. Now input goes to A, B and C.
|
||||
3. in tab 1, switch to pane D. Now input goes to A, B and D.
|
||||
4. switch to tab 2, pane E. Now input goes to A, B and E.
|
||||
|
||||
You can't have like, a set with A & B (in 1), then E & F (in 2). So if someone
|
||||
wants to type to both panes in 1, then both panes in 2, then both panes in 1,
|
||||
they need to keep toggling which panes are in the broadcast set.
|
||||
|
||||
#### What would this mean for PR #9222?
|
||||
|
||||
Similar to Proposal 1, we'd use `tab` as the default value for `scope`. In the
|
||||
future, when we add support for the other scopes, we'd change how the
|
||||
broadcasting works, to use a set of panes to broadcast to, instead of just the
|
||||
tab-level property.
|
||||
|
||||
### Proposal 3: It's iTerm2, but slightly different
|
||||
|
||||
While typing this up, I thought maybe it might make more sense if we took the
|
||||
iTerm2 version, and changed it slightly:
|
||||
|
||||
* `"scope": "tab"`: If all the panes are in the broadcast set for this tab, then
|
||||
remove them all. Otherwise, add all the panes in this tab to this tab's
|
||||
broadcast set.
|
||||
* `"scope": "pane"`: If this pane is in the broadcast set for a tab, then remove
|
||||
it. Otherwise, add it.
|
||||
|
||||
With this, we get rid of the tab-level setting for "broadcast to all the panes
|
||||
in this tab", and rely only on the broadcast set for that tab.
|
||||
|
||||
#### Pros:
|
||||
* All the pros from proposal A
|
||||
* Does away with the seemingly weird toggling between "all the panes in a tab"
|
||||
and "some of the panes in a tab" that's possible with proposal A
|
||||
|
||||
#### Cons:
|
||||
* You can't broadcast to a subset of panes in inactive tabs, in addition to
|
||||
the active tab. All panes you want to broadcast to must be in the active
|
||||
tab.
|
||||
* is _slightly_ different from iTerm2. Just _slightly_.
|
||||
* Does creating a new split in a pane that's being broadcast to add that pane to
|
||||
the broadcast set?
|
||||
|
||||
#### What would this mean for PR #9222?
|
||||
|
||||
Same as with proposal A, we wouldn't change anything in the current PR. A future
|
||||
PR that would add the other scope's to that action would need to change how the
|
||||
broadcasting within a tab works, to use a set of panes to broadcast to, instead
|
||||
of just the tab-level property.
|
||||
|
||||
## Conclusion
|
||||
|
||||
I'm proposing these settings for broader discussion. I'm not really sure which I
|
||||
like most at this point. 1 & 3 have the advantage of being most similar to the
|
||||
prior art, but 2 is more easily extendable to "groups" (see [Future
|
||||
Considerations](#Future-Considerations)).
|
||||
|
||||
**TODO!**: Make a decision.
|
||||
|
||||
_**Fortunately**_: All these proposals actually use the same set of actions. So
|
||||
it doesn't _really_ matter which we pick right now. We can unblock [#9222] as
|
||||
the implementation of the `"tab"` scope, and address other scopes in the future.
|
||||
We should still decide long-term which of these we'd like, but the actions seem
|
||||
universal.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
This is supposed to be a quick & dirty spec, so I'm LARGELY skipping this.
|
||||
|
||||
As far as indicators go, we'll throw something like:
|
||||
|
||||

|
||||
|
||||
in the tab when a pane is being broadcasted to. If all tabs are being
|
||||
broadcasted to, then they'll all have that icon. If a tab is inactive, and any
|
||||
pane in that tab is being broadcast to, then show the icon in the tab.
|
||||
|
||||
It probably makes the most sense to have pane titlebars ([#4998]) also display
|
||||
that icon.
|
||||
|
||||
In the original PR, it was suggested to use some variant of the [accent color]
|
||||
to on the borders of panes that are currently receiving broadcasted input. We're
|
||||
already using the accent color on the borders of the active pane.
|
||||
`SystemAccentColorLight*`/`SystemAccentColorDark*` would provide a way of using
|
||||
a similar hue with different lightness/saturation. This would be a decent visual
|
||||
indicator that they're _not_ the active pane, but they are going to receive
|
||||
input. Something a bit like:
|
||||
|
||||

|
||||
|
||||
This should obviously be able to be overridden in the user's theme, similar to
|
||||
the pane border colors.
|
||||
|
||||
iTerm2 also supports displaying "stripes" in the background of all the panes
|
||||
that are being broadcast too. That's certainly another way of indicating this
|
||||
feature to the user. I'm not sure how we'd layer it with the background image
|
||||
though. **I recommend we ignore this for now, and leave this as a follow-up**.
|
||||
|
||||
### Tab context menu items
|
||||
|
||||
For reference, refer to the following from iTerm2:
|
||||

|
||||
|
||||
We don't have a menu bar like on MacOS, but we do have a tab context menu. We
|
||||
could add these items as a nested entry under each tab. If we wanted to do this,
|
||||
we should also make sure to dynamically change the icon of the MenuItem to
|
||||
reflect the current broadcast state.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
<table>
|
||||
|
||||
<tr>
|
||||
<td><strong>Compatibility</strong></td>
|
||||
<td>
|
||||
|
||||
[comment]: # Will the proposed change break existing code/behaviors? If so, how, and is the breaking change "worth it"?
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
[comment]: # If there are any other potential issues, make sure to include them here.
|
||||
|
||||
## Implementation plan
|
||||
|
||||
* [ ] Resurrect [#9222], and use that to implement `"scope": "tab"`. This is
|
||||
implemented the same, regardless of which proposal we chose.
|
||||
* [ ] Add a tab context menu entry for toggling broadcast input, with a dynamic
|
||||
icon based on the current state.
|
||||
* [ ] Implement `"scope": "window"`. Again, this is implemented the same regardless
|
||||
of which proposal we pursue.
|
||||
* [ ] Decide between the two proposals here.
|
||||
* [ ] Implement `"scope": "pane"`.
|
||||
|
||||
Doing the first element here is probably the most important one for most users,
|
||||
and can be done regardless of the proposal chosen here. As such, we could even
|
||||
suggest the default value of `scope` be `tab`. If we did that, then we wouldn't
|
||||
need to do any args at all in the initial version.
|
||||
|
||||
## Future Considerations
|
||||
|
||||
Let's look to iTerm2, who's supported this feature for years, for some
|
||||
inspiration of future things we should be worried about. If their users have
|
||||
asked for these features, then it's inevitable that our users will too 😉
|
||||
|
||||
* [iterm2#6709] - Broadcast Input to multiple windows
|
||||
- This is pretty straightforward. Would require coordination with the Monarch
|
||||
though, and I'm worried about the perf hit of tossing every keystroke across
|
||||
the process boundary.
|
||||
- I suppose this would be `{ "action": "toggleBroadcastInput", "scope":
|
||||
"global" }`
|
||||
* [iterm2#6451], [iterm2#5563] - "Broadcast commands"
|
||||
- iTerm2 has an action that lets the user manually clear the terminal-side
|
||||
buffer. (This is tracked on the Windows Terminal as [#1882]). It might make
|
||||
sense for there to be a mode where some _actions_ are also broadcast to
|
||||
panes, not just key strokes. But which actions would those be? Moving the
|
||||
selection anchors? Copy doesn't really make sense. Paste _does_ though.
|
||||
Maybe the open find dialog / next&prev search match actions?
|
||||
- This probably would require it's own spec.
|
||||
* [iterm2#6007] - Different stripe color for different broadcast modes
|
||||
- Have one color to indicate when broadcasting in `global` scope, another in
|
||||
`tab` scope, a third in `pane` scope.
|
||||
- This might mesh well with theming ([#3327]), for properties like
|
||||
`pane.broadcastBorderColor.globalScope`,
|
||||
`pane.broadcastBorderColor.paneScope`. Don't love those names, but you get
|
||||
the idea.
|
||||
* **[iterm2#5639]: Broadcast groups**, [iterm2#3372] - Broadcast Input to
|
||||
multiple but not all tabs
|
||||
- This is probably the most interesting request. I think this one identifies a
|
||||
major shortcoming of the above proposals. With proposal 2, there's only ever
|
||||
one top-level broadcast group. With proposals 1 & 3, there's per-tab
|
||||
broadcast groups. In neither proposal can you have multiple concurrent
|
||||
side-by-side broadcast groups.
|
||||
- Groups should probably work across tabs. This would suggest that Proposal 2
|
||||
is closer to how groups would work. Instead of there being one top-level
|
||||
set, there would be multiple. **I'm not sure how proposals 1&3 would
|
||||
seamlessly transition into also supporting groups**.
|
||||
- The major trick here is: how do we differentiate these different groups to
|
||||
the user? If we used the broadcast icon with a number, maybe in the corner
|
||||
of the tab? Like [📡: 1]? Can a pane be in multiple broadcast sets at the
|
||||
same time?
|
||||
- The natural arg idea would be `{ "action": "toggleBroadcastInput", "scope":
|
||||
"tab", "group": 1 }` to say "add all panes in the tab to broadcast group 1",
|
||||
or "remove all panes in the tab from broadcast group 1". If panes are in
|
||||
another group, they'd be moved to the specified group. If all panes are in
|
||||
that group, then remove them all.
|
||||
- The UI for this would certainly get complex fast.
|
||||
- This also matches the Terminator-style broadcasting to groups.
|
||||
* Re: stripes in the background of the tab. We could expose a pane's current
|
||||
broadcast state to the pixel shader, and a user could use a custom pixel
|
||||
shader to add stripes behind the text in the shader code. That's one possible
|
||||
solution.
|
||||
|
||||
## Resources
|
||||
|
||||
[comment]: # Be sure to add links to references, resources, footnotes, etc.
|
||||
|
||||
|
||||
### Footnotes
|
||||
|
||||
<a name="footnote-1"><a>[1]:
|
||||
|
||||
|
||||
[#1882]: https://github.com/microsoft/terminal/issues/1882
|
||||
[#2634]: https://github.com/microsoft/terminal/issues/2634
|
||||
[#4998]: https://github.com/microsoft/terminal/issues/4998
|
||||
[#3327]: https://github.com/microsoft/terminal/issues/3327
|
||||
[#9222]: https://github.com/microsoft/terminal/pull/9222
|
||||
[this comment]: https://github.com/microsoft/terminal/issues/2634#issuecomment-789116413
|
||||
[iTerm2 implementation]: https://iterm2.com/documentation-one-page.html#documentation-menu-items.html
|
||||
[@zljubisic]: https://github.com/microsoft/terminal/pull/9222#issuecomment-789143189
|
||||
[accent color]: https://docs.microsoft.com/en-us/windows/uwp/design/style/color#accent-color-palette
|
||||
|
||||
|
||||
[iterm2#6709]: https://gitlab.com/gnachman/iterm2/-/issues/6709
|
||||
[iterm2#6451]: https://gitlab.com/gnachman/iterm2/-/issues/6451
|
||||
[iterm2#6007]: https://gitlab.com/gnachman/iterm2/-/issues/6007
|
||||
[iterm2#5639]: https://gitlab.com/gnachman/iterm2/-/issues/5639
|
||||
[iterm2#5563]: https://gitlab.com/gnachman/iterm2/-/issues/5563
|
||||
[iterm2#3372]: https://gitlab.com/gnachman/iterm2/-/issues/3372
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 336 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,380 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2021-08-31
|
||||
last updated: 2021-08-31
|
||||
issue id: #642
|
||||
---
|
||||
|
||||
# Buffer Exporting and Logging
|
||||
|
||||
## Abstract
|
||||
|
||||
A common user need is the ability to export the history of a terminal session to
|
||||
a file, for later inspection or validation. This is something that could be
|
||||
triggered manually. Many terminal emulators provide the ability to automatically
|
||||
log the output of a session to a file, so the history is always captured. This
|
||||
spec will address improvements to the Windows Terminal to enable these kinds of
|
||||
exporting and logging scenarios.
|
||||
|
||||
## Background
|
||||
|
||||
### Inspiration
|
||||
|
||||
Below are screenshots from the settings pages of three different terminal
|
||||
emulators with similar features - PuTTY, SecureCRT, and ConEmu:
|
||||
|
||||

|
||||
|
||||
_figure 1: PuTTY settings_
|
||||
|
||||

|
||||
|
||||
_figure 2: SecureCRT settings_
|
||||
|
||||

|
||||
|
||||
_figure 3: ConEmu settings_
|
||||
|
||||
These applications all offer some settings in common. Primarily, the important
|
||||
feature is the ability to specify a path to a log file which contains some
|
||||
special string formatting. This allows the user to log to different files based
|
||||
on the time & date of the session, or based on the session name.
|
||||
|
||||
### User Stories
|
||||
|
||||
* **Story A**: The user is able to use a context menu entry on the tab to export
|
||||
the contents of the buffer to a file, which they are prompted for.
|
||||
- This is explicitly what was requested in [#642]
|
||||
* **Story B**: The user can bind an action to export the contents of the buffer
|
||||
to a file, which they are prompted for.
|
||||
- Very similar to **A**, but via the command palette or a keybinding.
|
||||
* **Story C**: The user can export to an explicit file via an action
|
||||
- similar to **B**, but allowing for declaring the path to a file rather than
|
||||
prompting at runtime.
|
||||
* **Story D**: The user can choose to append to a file when exporting, rather
|
||||
than overwriting.
|
||||
* **Story E**: The user can specify a format string in the path to the file to
|
||||
export to, which the Terminal will automatically replace with variables like
|
||||
the time, date, and profile name.
|
||||
* **Story F**: When opening a specific profile, the user can automatically log
|
||||
to a file
|
||||
* **Story G**: The user can execute an action to start or stop logging to a
|
||||
given file.
|
||||
|
||||
|
||||
## Solution Design
|
||||
|
||||
I'm proposing the following actions and profile settings
|
||||
|
||||
* New Action: `exportBuffer()`.
|
||||
- Export the contents of the buffer to a file.
|
||||
- `path` (string, defaults to `""`): When empty, prompt the user for a name of
|
||||
a file to export to, using a file picker. This path accepts special
|
||||
formatting strings that will be substituted with certain variables
|
||||
(discussed [below](#path-formatting)).
|
||||
- `append` (boolean, defaults to `false`): When `false`, the file's contents
|
||||
will be overwritten. When `true`, the buffer contents will be appended to
|
||||
the end of the file.
|
||||
* New Profile Settings object: `logSettings`
|
||||
- This is an object that describes a set of behavior for logging a profile.
|
||||
- `path`: Same as the `path` in the `ExportBufferArgs` above
|
||||
- `append`: Same as the `append` in the `ExportBufferArgs` above
|
||||
- `captureAllOutput`: (boolean, defaults to `false`) When true, don't log only
|
||||
printable characters, also log non-printable escape characters written to
|
||||
the Terminal.
|
||||
- `captureInput`: (boolean, defaults to `false`) Additionally log input to the
|
||||
Terminal to the file. Input will be formatted as the traditional VT
|
||||
sequences, rather than the full `win32-input` encoding.
|
||||
- `newFileEveryDay`: (boolean, defaults to `false`) This requires the `day` to
|
||||
be an element of the path format string. When logging with this setting,
|
||||
opens a new file at midnight and starts writing that one.
|
||||
<!-- TODO! - `flushFrequently`: (boolean, defaults to `true`) -->
|
||||
* New Profile setting: `logAutomatically` (boolean, default `false`). When true,
|
||||
terminals with this profile will begin logging automatically.
|
||||
* New Action: `toggleLogging()`.
|
||||
- Start or stop logging to the configured file. If the terminal is already
|
||||
logging with different settings than in this action, then stop logging
|
||||
regardless (don't just start logging to the new file)
|
||||
- This action accepts all the same args the profile's `logSettings` object.
|
||||
- If _any_ args are provided, use those args. If _none_ are provided, then use
|
||||
the logging settings present in the profile (if there are any).
|
||||
- If there's not path provided (either in the args to the action or in the
|
||||
profile), prompt the user to pick a file to log to.
|
||||
|
||||
### Examples
|
||||
```json
|
||||
{
|
||||
"actions": [
|
||||
{ "keys": "f1", "command": "exportBuffer" },
|
||||
{ "keys": "f2", "command": { "action": "exportBuffer", "path": "c:\\logs\\${year}-${month}-${date}\\{profile}.txt" } },
|
||||
|
||||
{ "keys": "f3", "command": "toggleLogging" },
|
||||
{ "keys": "f4", "command": { "action": "toggleLogging", "path": "c:\\logs\\${profile}.log", "append": true } },
|
||||
],
|
||||
"profiles": [
|
||||
{
|
||||
"name": "foo",
|
||||
"logging": {
|
||||
"path": "c:\\foo.txt",
|
||||
"append": true
|
||||
},
|
||||
"automaticallyLog": false
|
||||
},
|
||||
{
|
||||
"name": "bar",
|
||||
"logging": {
|
||||
"path": "c:\\logs\\${date}\\bar.txt",
|
||||
"append": false
|
||||
},
|
||||
"automaticallyLog": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Revisiting our original stories:
|
||||
|
||||
* **Story A**: This is already implemented in [#11062]
|
||||
* **Story B**: This is the action bound to <kbd>f1</kbd>.
|
||||
* **Story C**: This is the action bound to <kbd>f2</kbd>.
|
||||
* **Story D**: This is the `append` property in the actions, profile settings.
|
||||
* **Story E**: An example of this is in the action bound to <kbd>f2</kbd>,
|
||||
<kbd>f4</kbd>, and in the profile "bar"'s logging settings.
|
||||
* **Story F**: The profile "bar" is configured to automatically log when opened.
|
||||
* **Story G**: This is the action bound to <kbd>f4</kbd>.
|
||||
|
||||
In addition,
|
||||
* When opening the profile "foo", it will not automatically log to a file.
|
||||
- Pressing <kbd>f3</kbd> will begin logging to `c:\foo.txt`
|
||||
- Pressing <kbd>f4</kbd> will begin logging to `c:\logs\foo.log`
|
||||
|
||||
### Path formatting
|
||||
|
||||
[TODO!]: # TODO!
|
||||
|
||||
For discussion: What syntax do we want?
|
||||
* PuTTY uses `&Y`, `&M`, `&D`, `&T`, `&H`, `&P` for year, month, day, time, host
|
||||
and port respectively.
|
||||
* SecureCRT uses:
|
||||
- `%H` – hostname
|
||||
- `%S` – session name
|
||||
- `%Y` – four-digit year
|
||||
- `%M` – two-digit month
|
||||
- `%D` – two-digit day of the month
|
||||
- `%h` – two-digit hour
|
||||
- `%m` – two-digit minute
|
||||
- `%s` – two-digit seconds
|
||||
- `%t` – three-digit milliseconds
|
||||
- `%%` – percent (%)
|
||||
- `%envvar%` – environment variable (for instance `%USERNAME%`)
|
||||
|
||||
We have some precedent for formatting with `${braces}`, a la the iterable
|
||||
command in the Command Palette (e.g `${profile.name}`). Additionally, [#9287]
|
||||
implements support for environment variables in the Terminal with the
|
||||
`${env:VARIABLE}` syntax.
|
||||
|
||||
What variables do we want exposed, and how do we want users to be able to format
|
||||
them?
|
||||
|
||||
This doc was initially authored assuming we'd go with a `${braces}` syntax, like:
|
||||
- `${profile}` – profile name
|
||||
- `${year}` – four-digit year
|
||||
- `${month}` – two-digit month
|
||||
- `${day}` – two-digit day of the month
|
||||
- `${hour}` – two-digit hour
|
||||
- `${minute}` – two-digit minute
|
||||
- `${second}` – two-digit second
|
||||
- `${ms}` – three-digit milliseconds
|
||||
- `${env:variable}` – environment variable (for instance `${env:USERPROFILE}`)
|
||||
(inspired by [#9287])
|
||||
|
||||
### Exporting vs Logging
|
||||
As far as specific implementation details goes, exporting is the easier work to
|
||||
do. [#11062] already wires up the `TerminalApp` to retrieve the buffer contents
|
||||
from the `TermControl`, so writing them at request is easy.
|
||||
|
||||
Logging is harder. We don't want the `TermControl` telling the `TerminalApp`
|
||||
layer about every piece of output logged. Especially in the post-[#5000] world
|
||||
where that's a cross-process hop. Instead, we'll want the `ControlCore` /
|
||||
`ControlInteractivity` to do _logging_ themselves.
|
||||
|
||||
### Logging Mechanics
|
||||
|
||||
#### When do we log?
|
||||
|
||||
[TODO!]: # TODO!
|
||||
|
||||
When do we decide to actually log? Take for example typing in a `pwsh` or
|
||||
`bash` prompt. Imagine the user types
|
||||
<kbd>w</kbd><kbd>h</kbd><kbd>a</kbd><kbd>t</kbd>, then hits
|
||||
<kbd>Bksp</kbd><kbd>Bksp</kbd>, such that the prompt is just `wh`. What should
|
||||
the log contain? `what^h ^h^h ^h`<sup>[[1]](#footnote-1)</sup>? `wh`?
|
||||
|
||||
My worry with logging the backspaces is that conpty is sometimes a bit noisier
|
||||
than it needs to be with using `^H` as a cursor positioning sequence. Should we
|
||||
only log lines when the cursor newlines or otherwise moves from the line it is
|
||||
currently on?
|
||||
|
||||
I'll need to look at what PuTTY emits for the "Printable output" option.
|
||||
|
||||
#### What happens when we _start_ logging?
|
||||
|
||||
If the user has a terminal that did not start with logging enabled, but then
|
||||
started logging with `toggleLogging`, what should we log? All future output? Or
|
||||
should we log the current buffer contents as well?
|
||||
|
||||
I'm inclined to lean towards simply "all future output", and ignore any current
|
||||
buffer content. If the user rally wants to log the current buffer contents _and_
|
||||
start logging, they can use a `multipleActions` action ([#11045]) to
|
||||
`exportBuffer` to a file, then `toggleLogging` to that same file with
|
||||
`"append":true`.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
<table>
|
||||
|
||||
<tr>
|
||||
<td><strong>Compatibility</strong></td>
|
||||
<td>
|
||||
|
||||
Since this functionality is entirely new, nothing here should negatively affect
|
||||
existing functionality.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Performance, Power, and Efficiency</strong></td>
|
||||
<td>
|
||||
|
||||
When logging, it's expected there will be a measurable performance hit. We can
|
||||
try to mitigate this by only writing to the file on a background thread,
|
||||
separate from the connection or rendering thread. Since auto-logging will only
|
||||
take place in the content process, we're not worried about the file writing
|
||||
occurring on the UI thread.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Also frequently requested is the ability to log timestamps of when commands are
|
||||
executed. I don't think that this is a valuable feature for the Terminal to
|
||||
implement ourselves. Windows Terminal is fundamentally just a _terminal
|
||||
emulator_, it doesn't really know what's going on behind the scenes with
|
||||
whatever client application (`cmd`, `powershell`, `bash`, `vim`) that is
|
||||
connected to it. WT doesn't know when the user is typing in commands to the
|
||||
shell, or if the user is just typing in text in `emacs` or something. There's no
|
||||
way for the terminal to know that. It's _typically_ the client application's
|
||||
responsibility to save it's own command history. `bash` and `powershell` both do
|
||||
a pretty good job of saving this to another file to restore across sessions,
|
||||
while `cmd.exe` doesn't.
|
||||
|
||||
Windows is a messy world and this model gets a little tricky here. `cmd.exe`
|
||||
isn't actually managing it's own command history _at all_. `conhost` is doing
|
||||
that work on behalf of the client applications. Some long time ago someone
|
||||
thought it would be a good idea to have the `readline` functionality baked
|
||||
directly into the console host. Whether that was a good idea or not remains to
|
||||
be seen - it's certainly made things like `python.exe`'s REPL easier to
|
||||
implement, since they don't need to maintain their own history buffer, but it
|
||||
makes it hard to de-tangle behavior like this from the console itself.
|
||||
|
||||
I'm not sure how it would be possible to add a keybinding to the Windows
|
||||
Terminal that would be able to save the console's _command_ history. Especially
|
||||
considering the Terminal might _not_ be connected to a console host session at
|
||||
all. If the Windows Terminal were directly running a `wsl` instance (something
|
||||
that's not possible today, but something we've considered adding in the future),
|
||||
then there wouldn't be a `conhost` in the process tree at all, and now
|
||||
requesting the command history from the console wouldn't work _mysteriously_.
|
||||
|
||||
Furthermore, shells can always be configured to emit timestamps in their prompts
|
||||
themselves. Since the Terminal has no knowledge of when a command is actually
|
||||
entered, but the _shell_ does, it makes the most sense to configure the user's
|
||||
_shell_ to emit that information. The Terminal will then dutifully log that
|
||||
output along with everything else.
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
Below is a rough outline of how I'd go about implementing these features. Each
|
||||
lop-level checkbox could be its own PR, following from [#11062].
|
||||
|
||||
### Buffer exporting
|
||||
|
||||
* [ ] Add an `exportBuffer()` action that opens the file picker
|
||||
* [ ] Add a string `path` parameter to `exportBuffer()` that allows the user to
|
||||
press a key and immediately export the buffer to a whole path
|
||||
- default to `""`, which indicates "open the file picker"
|
||||
* [ ] add a boolean `append` (default to `false`) parameter to `exportBuffer`.
|
||||
When true, export to the file given by appending, not overwriting the file
|
||||
* [ ] Enable string formatting in the `path` parameter.
|
||||
- What format do we want? `yyyy-mm-dd`? `%Y-%m-%D`? `&Y-&m-&D`? `${year}-${month}-${day}`?
|
||||
- What are all the variables we want?
|
||||
- Year, month, day, hour, minute - those are easy
|
||||
- `WT_SESSION`, for a uuid for each session maybe?
|
||||
- Profile name perhaps? Commandline?
|
||||
* [ ] more...
|
||||
|
||||
### Automatic logging
|
||||
|
||||
* [ ] `toggleLogging()` Action for start/stop logging, with `path`, `append`
|
||||
properties (like `exportBuffer()`)
|
||||
- `ToggleLoggingArgs` contains a single member `LoggingSettings`, which
|
||||
contains `path` and `append` properties. This will make sense below.
|
||||
* [ ] add `LoggingSettings` property for "log all output" (default would just be
|
||||
"log printable output")
|
||||
* [ ] add `LoggingSettings` property for "log input" (Though, we'd probably want
|
||||
to log it as normal VT encoded, not as `win32-input` encoded)
|
||||
* [ ] Per-profile setting for `logSettings`, which can contain an entire
|
||||
`LoggingSettings` (like the `ToggleLoggingArgs`). When `toggleLogging` with no
|
||||
args, try to use the profile's `loggingSettings` instead.
|
||||
* [ ] Per-profile setting for `automaticallyLog`, which would log by default
|
||||
when the profile is opened
|
||||
* [ ] `LoggingSettings` property for "New file every day", which only works when
|
||||
the `{day}` is in the path string. When auto-logging with this setting, opens
|
||||
a new file at midnight and starts writing that one.
|
||||
<!-- * [ ] `LoggingSettings` property for "Flush log frequently", defaults to
|
||||
`true`(?). This causes us to flush all output to the file, instead of just...
|
||||
on close? on newline? It's unclear exactly when PuTTY flushes with this off.
|
||||
Need more coffee. -->
|
||||
|
||||
### Future Considerations
|
||||
|
||||
* When logging begins, the Terminal could display a toast for "Logging to
|
||||
{filename}", and a similar one for "Stopped logging to {filename}".
|
||||
* There's no good way of displaying a UI element to indicate that a pane is
|
||||
currently logging to a file. I don't believe PuTTY displays any sort of
|
||||
indicator. SecureCRT only displays a checkbox within the context menus of the
|
||||
application itself.
|
||||

|
||||
|
||||
Maybe when logging to a file, we could replace the "Export Text" context menu
|
||||
entry with "Stop Logging"
|
||||
* We could maybe add a setting to disable logging from the alt buffer. This
|
||||
might help make this setting more valuable for users who are using full-screen
|
||||
applications like `vim`. Since those applications redraw the entire viewport
|
||||
contents frequently, the log might be unnecessarily noisy. Disabling logging
|
||||
while in the alt buffer would show that the user opened vim, and then they did
|
||||
some things after vim exited.
|
||||
* Logging all output will be VERY helpful to us in the future for trying to
|
||||
recreate bugs on our end that users can repro but we can't!
|
||||
|
||||
## Resources
|
||||
|
||||
PuTTY Logging documentation: https://tartarus.org/~simon/putty-snapshots/htmldoc/Chapter4.html#config-logfilename
|
||||
ConEmu Logging documentation: https://conemu.github.io/en/AnsiLogFiles.html
|
||||
|
||||
### Footnotes
|
||||
|
||||
<a name="footnote-1"><a>[1]: Remember that `^H` is non-destructive, so the
|
||||
sequence `what^h ^h^h ^h` is can be read as:
|
||||
* print "what"
|
||||
* move the cursor back one
|
||||
* print a space (overwriting 't')
|
||||
* move the cursor back one (now it's on the space where 't' was)
|
||||
* move the cursor back one
|
||||
* print a space (overwriting 'a')
|
||||
* move the cursor back one (now it's on the space where 'a' was)
|
||||
|
||||
[#642]: https://github.com/microsoft/terminal/issues/642
|
||||
[#5000]: https://github.com/microsoft/terminal/issues/5000
|
||||
[#9287]: https://github.com/microsoft/terminal/pull/9287
|
||||
[#11045]: https://github.com/microsoft/terminal/pull/11045
|
||||
[#11062]: https://github.com/microsoft/terminal/pull/11062
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -60,7 +60,7 @@ it could host any arbitrary content.
|
||||
|
||||
### Security
|
||||
|
||||
I don't forsee this implementation by itself raising security concerns. This
|
||||
I don't foresee this implementation by itself raising security concerns. This
|
||||
feature is mostly concerned with adding support for arbitrary controls, not
|
||||
actually implementing some arbitrary controls.
|
||||
|
||||
|
||||
217
doc/terminal-a11y-2023.md
Normal file
217
doc/terminal-a11y-2023.md
Normal file
@@ -0,0 +1,217 @@
|
||||
# Windows Terminal Accessibility
|
||||
## Abstract
|
||||
The release of Windows Terminal has served as a way to reinvigorate the command-line ecosystem on Windows by providing a modern experience that is consistently updated. This experience has caused faster innovation in the Windows command-line ecosystem that can be seen across various areas like expanded virtual terminal sequence support and enhanced accessibility. Command-line apps can now leverage these innovations to create a better experience for the end-user.
|
||||
|
||||
Since accessibility is a very broad area, this document is intended to present recent innovations in the accessibility space for the command-line ecosystem. Furthermore, it will propose additional improvements that can be made alongside key stakeholders that could benefit from such changes.
|
||||
|
||||
## Windows Command-line Ecosystem
|
||||
### First-party terminals
|
||||
For many years, Console Host (Conhost) was the only first-party terminal on Windows. In 2019, Windows Terminal was released to the world as an open source first-party terminal. Windows Terminal was distributed through the Microsoft Store and received regular updates throughout the year, much more frequently than Conhost. In October 2022, Windows Terminal was enabled as the default terminal on Windows.
|
||||
|
||||
A significant amount of code is shared between Conhost and Windows Terminal to create the terminal area. To enable an accessible experience for this area, a shared UI Automation provider was introduced in 2019[^1], enabling accessibility tools to navigate and read contents from the terminal area. In 2020, Windows Terminal was updated to dispatch UIA events signaling when the cursor position, text output, or selection changed; this left the work of identifying what changed in the output to the attached screen reader application[^2]. In 2022, Windows Terminal was updated to dispatch UIA notifications with a payload of what text was written to the screen[^3].
|
||||
|
||||
### Internal Partners
|
||||
There are many first-party command-line applications on Windows. The following are a few examples of those that are regularly updated:
|
||||
- [**GitHub CLI**](https://cli.github.com/): a tool that can be used to query and interact with GitHub repos (open source)
|
||||
- [**Winget**](https://github.com/microsoft/winget-cli): a tool to install applications and other packages
|
||||
- [**PSReadLine**](https://github.com/PowerShell/PSReadLine): a PowerShell module that enhances the input line experience
|
||||
- [**Windows Subsystem for Linux (WSL)**](https://learn.microsoft.com/en-us/windows/wsl/): a tool to manage and run GNU/Linux environments without a traditional virtual machine
|
||||
- [**PowerShell**](https://github.com/PowerShell/PowerShell): a cross-platform command-line shell (open source)
|
||||
|
||||
Additionally, CMD is a Windows-specific command-line shell. Though it is no longer being updated, it is still widely used.
|
||||
|
||||
### External Command-line Applications
|
||||
There are many third-party command-line applications on Windows that are regularly updated.
|
||||
The following examples take over the entire viewport:
|
||||
- [**Vim**](https://www.vim.org/): a modal-based text editor with an extensive plugin system
|
||||
- **Emacs**: a shortcut-oriented text editor with an extensive plugin system
|
||||
- [**Emacspeak**](https://emacspeak.sourceforge.net/): an Emacs subsystem that leverages Emacs' context-specific information to produce speech output
|
||||
- **Midnight Commander**: a file manager with an extensive text user interface
|
||||
- **Far Manager**: a file manager for Windows with an exclusive text user interface
|
||||
- **tmux**: a terminal multiplexer
|
||||
The following examples don't take over the entire viewport:
|
||||
- [**Oh My Posh**](https://ohmyposh.dev/): a tool to customize shell prompts
|
||||
- **git**: a tool for version control
|
||||
The following examples operate as command-line shells:
|
||||
- [**Bash**](https://www.gnu.org/software/bash/) is the default shell for most Linux distributions
|
||||
- [**Fish shell**](https://fishshell.com/) provides a rich shell experience with features like autosuggestion support and VGA colors
|
||||
- [**Z shell**](https://zsh.sourceforge.io/) is an extended Bourne shell
|
||||
|
||||
## Accessibility tools and features
|
||||
### Screen Readers
|
||||
Windows is predominantly dominated by three major screen readers: Narrator, JAWS, and NVDA.
|
||||
- **Narrator**: a free first-party screen reader that ships with Windows.
|
||||
- **JAWS**: a third-party screen reader that requires an annual license to use.
|
||||
- **NVDA**: a free third-party screen reader that is open source.
|
||||
It's important to note that most users generally use NVDA or JAWS[^4], so it's very important to consider the experience under both of those screen readers.
|
||||
|
||||
### Miscellaneous User Tools
|
||||
Windows has many built-in features that enable a more accessible experiences. The following is a list of Windows accessibility settings and tools that may have an impact on apps:
|
||||
- **Text size setting**: dictates the default size of text throughout all of Windows
|
||||
- **Always show scrollbars setting**: forces the scrollbars to always be expanded and shown. Generally, this setting is automatically respected when using the Windows UI library.
|
||||
- **Transparency effects setting**: allows transparency effects within the Windows shell
|
||||
- **Animation effects setting**: allows animation effects
|
||||
- **Text cursor indicator**: displays a colorful UI over the text cursor to make it stand out more and easier to find. Note, this is powered by UI Automation selection changed events because an empty selection is the cursor's position.
|
||||
- **Magnifier**: zooms the monitor to provide a closer look at the current mouse and cursor position. Note, this is powered by UI Automation selection changed events because an empty selection is the cursor's position.
|
||||
- **Color filters**: manipulates displayed colors to address color-blindness
|
||||
- **High contrast themes**: ensures all displayed content has a high contrast ratio. Generally, this setting is automatically respected when using the Windows UI library. However, non-WinUI surfaces (like the terminal area) need to do additional work to respect this.
|
||||
- **Voice access**: enables users to interact with their PC using their voice. This is powered by UI Automation to identify what controls can be interacted with and how.
|
||||
|
||||
### Verification Tools
|
||||
[Accessibility Insights](https://accessibilityinsights.io/) is a modern tool that can be used to test and understand accessibility issues on Windows. It comes with a built-in color contrast analyzer, a UI Automation event listener, and a UI Automation tree explorer. Additionally, it can be used to explore and interact with different control patterns using the same API screen readers and other accessibility tools rely on. Accessibility Insights is also capable of running automated tests in CI and locally to detect common and simple issues.
|
||||
|
||||
## Proposed Accessibility Improvements
|
||||
|
||||
### Respect Text Size OS setting
|
||||
Windows Terminal has a profile setting for the font size (`"font"."size": <integer>`) which is then used when instantiating new terminal sessions. There should be a way to make the terminal font size respect the text size setting in Windows found in the Settings App > Accessibility > Text Size.
|
||||
|
||||
A possible solution is to scale the session's text size at runtime based on the OS setting. Furthermore, the Windows Terminal's settings UI should warn the user when they are attempting to modify the font size profile setting.
|
||||
|
||||
This is tracked by [Windows accessibility "text size" parameter ignored by terminal · Issue #13600](https://github.com/microsoft/terminal/issues/13600)
|
||||
|
||||
### Respect High Contrast Mode
|
||||
When high contrast mode is enabled, any WinUI surfaces are automatically changed to respect the newly desired contrast mode. However, the terminal area does not. In 2021, the Delta E algorithm was added to Windows Terminal as a tool to improve color contrast in the terminal area if desired[^5]. In 2022, it was exposed via an `adjustIndistinguishableColors` profile setting with a variety of possible configurations[^6].
|
||||
|
||||
There are several possible solutions to this issue, which all should be exposed to the user. Such solutions include the following:
|
||||
- Enabling `adjustIndistinguishableColors` automatically when high contrast mode is enabled
|
||||
- This leverages the Delta E algorithm to force an adequate contrast ratio. The algorithm would need to be expanded to expose the threshold.
|
||||
- Reducing the colors used to match the Windows Contrast Theme colors
|
||||
- Edge uses this heuristic and it has the added benefit that Windows Terminal is respecting the user's high contrast theme set in the OS.
|
||||
- Implementing the `DECSTGLT` escape sequence which enables switching between different color modes (one of which is monochrome)
|
||||
Additionally, we should automatically make the terminal fully opaque and ignore the acrylic or traditional transparency setting.
|
||||
|
||||
This is tracked by [[Epic] Improved High Contrast support · Issue #12999](https://github.com/microsoft/terminal/issues/12999)
|
||||
|
||||
### Enhanced UI Automation movement mechanisms
|
||||
UI Automation has an `ITextRangeProvider` interface that can be used to represent a span of text. These text ranges can then be manipulated to explore the text area. Such manipulations include moving either (or both) endpoint(s) to encompass a text unit or move by unit. UI Automation supports the following text units:
|
||||
- Character
|
||||
- Format
|
||||
- Word
|
||||
- Line
|
||||
- Paragraph
|
||||
- Page
|
||||
- Document
|
||||
Windows Terminal and Conhost implement this `ITextRangeProvider` interface. However, this implementation doesn't support all of the available text units. Though this is standard across other implementations, this provides an opportunity for growth.
|
||||
|
||||
#### UI Automation: Movement by page
|
||||
Movement by page is a relatively abstract concept in terminals. Arguably, the viewport could be leveraged to be considered a "page", which would provide users with a quick way to navigate the buffer.
|
||||
|
||||
This is tracked by [UIA: support movement by page · Issue #13756](https://github.com/microsoft/terminal/issues/13756)
|
||||
|
||||
#### UI Automation: Movement by format
|
||||
The terminal area supports various text decorations including but not limited to underlined, bold, and italic text. These text attributes were exposed via the `ITextRangeProvider` interface in [PR #10366](https://github.com/microsoft/terminal/pull/10336). UI automation has support for navigating through contiguous spans adhering to a text attribute, which could be useful to expose to UIA clients.
|
||||
|
||||
This is tracked by [UIA Formatted Text Navigation · Issue #3567](https://github.com/microsoft/terminal/issues/3567).
|
||||
|
||||
#### Movement by prompt
|
||||
[PR #12948](https://github.com/microsoft/terminal/pull/12948) added support for scrollbar marks in Windows Terminal. As a part of this, an `experimental.autoMarkPrompts` profile setting registers scrollbar marks with each prompt when enabled. This is a fantastic way to navigate through the scroll history quickly and easily.
|
||||
|
||||
Though UI automation doesn't have a text unit for prompts, we could define a "paragraph" as the space between two prompts.
|
||||
|
||||
This issue is tracked by [megathread: Scrollbar Marks · Issue #11000](https://github.com/microsoft/terminal/issues/11000).
|
||||
|
||||
### Mark Mode support for degenerate range
|
||||
[PR #13053](https://github.com/microsoft/terminal/pull/13053) added support for mark mode in Windows Terminal. Mark mode allows users to create and modify selections by exclusively using the keyboard. However, screen reader users have reported it as a strange experience because it always has a cell of text selected; this results in the screen reader reading "x selected, y unselected" as opposed to the expected "x" when moving the cursor around.
|
||||
|
||||
Unfortunately, the changes required to fix this are very extensive because selections are stored as two inclusive terminal coordinates, which makes it impossible to represent an empty selection.
|
||||
|
||||
This is tracked by [A11y: windows terminal emits selection/deselection events in mark mode when navigating with arrow keys · Issue #13447](https://github.com/microsoft/terminal/issues/13447).
|
||||
|
||||
### Search improvements
|
||||
The search dialog can be used to perform a runtime query of the text buffer. However, in its current form, it does not count the total number of matches like other developer tools (i.e. Visual Studio Code). This work item tracks two things:
|
||||
1. Support for counting the total number of matches
|
||||
2. Displaying the number of results in the search box and announcing it via a UIA notification to the screen reader
|
||||
Additional search features may fall into this including but not limited to:
|
||||
- Highlight the search results in the scroll bar
|
||||
- Successful searches should read the line where text was found (similar to VS Code experience)
|
||||
|
||||
It seems that UI Automation has no guidance for a consistent, good search experience on Windows. Noting that VS Code and Microsoft Word have two different search experiences, it may be valuable to create documentation and guidance for other developer tools on Windows to follow, thus helping to create a more consistent search experience.
|
||||
|
||||
This is tracked by [Search should display the number of results it finds · Issue #6319](https://github.com/microsoft/terminal/issues/6319) and [[JAWS] Search results read "results found" instead of something useful · Issue #14153](https://github.com/microsoft/terminal/issues/14153).
|
||||
|
||||
### Shell suggestions
|
||||
Shells provide different forms of autocompletion. However, that autocompletion isn't necessarily an accessible experience. For example, CMD's standard autocompletion experience rewrites the "word". The screen reader rereads the new "word" properly, however, the user must cycle between the options until the desired one is shown. As another example,
|
||||
|
||||
PowerShell supports menu completion, where possible completions are displayed as a table of text and relevant help text is displayed. However, screen readers struggle with this because the entire menu is redrawn every time, making it harder to understand what exactly is "selected" (as the concept of selection in this instance is a shell-side concept represented by visual manipulation).
|
||||
|
||||
A possible solution is to introduce a new VT sequence to have the shell provide a payload. This payload can contain things like the suggested completion as well as associated help text. Such data can then be leveraged by Windows Terminal to create UI elements. Doing so leverages WinUI's accessible design. By designing a new VT sequence, other terminal emulators and CLI apps can opt-in to this functionality too.
|
||||
|
||||
This is tracked by [Enhance shell autocompletion with a cool new user interface and shell completion protocol · Issue #3121](https://github.com/microsoft/terminal/issues/3121).
|
||||
|
||||
### Scripts Panel
|
||||
Common command-line experiences revolve around inputting a command into the shell and executing it. Expert command-line users can remember complex commands off the top of their heads. Windows Terminal could help users by storing complex commands. Furthermore, these commands should be able to be easily shared between users.
|
||||
|
||||
From an accessibility standpoint, this can be very useful for users with mobility issues, as it is particularly difficult to write complex commands quickly.
|
||||
|
||||
This is tracked by [Feature Request - Scripts Panel · Issue #1595](https://github.com/microsoft/terminal/issues/1595).
|
||||
|
||||
### Broadcast Input
|
||||
Windows Terminal supports running multiple sessions at once across various panes, tabs, and windows. The broadcast input feature allows users to send input to multiple sessions at once.
|
||||
|
||||
From an accessibility standpoint, this can be very useful for users with mobility issues, as it is time consuming to write commands to multiple sessions quickly, particularly if those commands are complex.
|
||||
|
||||
This is tracked by [Support broadcast input? · Issue #2634](https://github.com/microsoft/terminal/issues/2634).
|
||||
|
||||
### UI Automation Notification Related Improvements
|
||||
In 2022, Windows Terminal added UI Automation notifications that contained a payload of text output[^3]. This was done for various reasons. For one, Narrator was not reading new output by the terminal, and would require changes on their end to accomplish this. Another reason is that NVDA is unable to handle too many text changed events, which Conhost and Windows Terminal are both culprits of since they dispatch an event when text is written to the terminal output.
|
||||
|
||||
UIA notifications have provided many compatibility benefits since screen readers automatically read notifications they receive. Additionally, this has provided the possibility for major performance enhancements as screen readers may no longer be required to diff the text buffer and figure out what has changed. NVDA has prototyped listening to notifications and ignoring text changed events entirely[^7]. However, it reveals underlying challenges with this new model such as how to handle passwords. The proposals listed in this section are intended to have Windows Terminal achieve improved performance and accessibility quality.
|
||||
|
||||
#### VT Screen Reader Control
|
||||
Some command-line applications are simply too difficult to create a consistent accessible experience. Applications that draw decorative content, for example, may have that content read by a screen reader.
|
||||
|
||||
In 2019, Daniel Imms wrote a spec proposing a VT sequence that can partially control the attached screen reader[^8]. This VT sequence consists of three main formats:
|
||||
1. Stop announcing incoming data to the screen reader. The screen reader will resume announcing incoming data if any key is pressed.
|
||||
2. Resume announcing incoming data to the screen reader.
|
||||
3. Announce the associated string payload immediately.
|
||||
Additionally, all three formats include a string payload that will be announced immediately by the screen reader (as is done with the third format).
|
||||
|
||||
JAWS and Narrator both immediately read UIA notifications as that is how Windows Terminal presents newly output text. As described earlier, NVDA currently has notifications disabled, but is prototyping moving towards a world where they can drop text diffing entirely in favor of this.
|
||||
|
||||
With Windows Terminal now dispatching UIA notifications, it would be relatively trivial for Windows Terminal to extract the string payload from the relevant VT sequences and present it in a new UIA notification. Additionally, an internal flag would be enabled to suppress UIA notifications for text output until the flag is flipped via a key press or the relevant VT sequence is received.
|
||||
|
||||
This is tracked by [Unable to use applications that hook the arrow keys using Windows Console Host. · Issue #13666](https://github.com/microsoft/terminal/issues/13666)
|
||||
|
||||
#### Screen Reader Backpressure Control Proposal
|
||||
NVDA has reported issues where Windows Terminal sends too may text changed events at once, causing NVDA to hang. Several considerations have been made in this area to address this issue:
|
||||
- **Batch notifications**: unhelpful because Terminal doesn't know how much text is intended to be output
|
||||
- **Diff text before sending a notification**: same problem as above
|
||||
- **Provide an API to throttle notifications**: causes a security risk for denial of service attacks
|
||||
Surprisingly, UI Automation doesn't have a built-in way to handle backpressure. However, the appropriate fix here seems to be either on the UI Automation side to fix this for all UIA clients, or on the NVDA side to improve event handling.
|
||||
|
||||
This is tracked by [UIA: reduce number of extraneous text change events in conhost · Issue #10822](https://github.com/microsoft/terminal/issues/10822)
|
||||
|
||||
## Proposed Priorities
|
||||
The following table assigns priorities to the aforementioned work items. These priorities are solely based on accessibility impact to improve the Windows command-line ecosystem.
|
||||
|
||||
| Priority | Work Item | Reasoning |
|
||||
|----------|-----------|-----------|
|
||||
| 1 | VT Screen Reader Control | Several partners (both CLI apps and UIA clients) would benefit from this feature and have expressed interest in adopting it. |
|
||||
| 1 | Shell suggestions | Several partners (both CLI apps and UIA clients) would benefit from this feature and have expressed interest in adopting it. |
|
||||
| 1 | Search improvements | Search is very difficult to use in its current implementation. |
|
||||
| 1 | Mark Mode support for degenerate range | Experience is barely usable and very unfriendly for non-sighted users. This is the only non-mouse method to select text. |
|
||||
| 2 | Respect High Contrast Mode | Clear workarounds exist, but the golden path scenario fails where a user enables high contrast mode and content does not respect it. |
|
||||
| 2 | Text Size OS Setting | Clear workarounds exist, but the golden path scenario fails where a user enables an OS setting and content does not respect it. |
|
||||
| 2 | Broadcast Input | Users with mobility issues would greatly benefit from this. |
|
||||
| 3 | Scripts Panel | Send-input commands currently exist. This would just expose it better. |
|
||||
| 3 | UIA: Move to prompt | Additional UIA text unit support is good. |
|
||||
| 3 | UIA: Move by page | Additional UIA text unit support is good. |
|
||||
| 3 | UIA: Move by format | Additional UIA text unit support is good. |
|
||||
|
||||
Generally, the reasoning behind these priorities can be broken down as follows:
|
||||
- Priority 1 bucket:
|
||||
- These work items provide access to features within the command-line ecosystem that are practically unusable for users with accessibility needs.
|
||||
- Priority 2 bucket:
|
||||
- Active workarounds exist for these issues, but they could be cumbersome.
|
||||
- Priority 3 bucket:
|
||||
- Purely innovative ideas that provide an exceptional experience for users.
|
||||
|
||||
## References
|
||||
[^1]: [Accessibility: Set-up UIA Tree by carlos-zamora · Pull Request #1691](https://github.com/microsoft/terminal/pull/1691)
|
||||
[^2]: [Fire UIA Events for Output and Cursor Movement by carlos-zamora · Pull Request #4826](https://github.com/microsoft/terminal/pull/4826)
|
||||
[^3]: [Use UIA notifications for text output by carlos-zamora · Pull Request #12358](https://github.com/microsoft/terminal/pull/12358)
|
||||
[^4]: [WebAIM: Screen Reader User Survey #8 Results](https://webaim.org/projects/screenreadersurvey8/#:~:text=NVDA%20is%20now%20the%20most%20commonly%20used%20screen,respondents%20use%20more%20than%20one%20desktop%2Flaptop%20screen%20reader.)
|
||||
[^5]: [Implement the Delta E algorithm to improve color perception by PankajBhojwani · Pull Request #11095](https://github.com/microsoft/terminal/pull/11095)
|
||||
[^6]: [Change AdjustIndistinguishableColors to an enum setting instead of a boolean setting by PankajBhojwani · Pull Request #13512](https://github.com/microsoft/terminal/pull/13512)
|
||||
[^7]: [Prototype for Windows Terminal: Use notifications instead of monitoring for new text by leonardder · Pull Request #14047 · nvaccess/nvda (github.com)](https://github.com/nvaccess/nvda/pull/14047)
|
||||
[^8]: [Control Screen Reader from Applications (#18) · Issues · terminal-wg / specifications · GitLab](https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/18)
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
|
||||
#ifdef USE_INTERVAL_TREE_NAMESPACE
|
||||
namespace interval_tree
|
||||
|
||||
@@ -8,7 +8,7 @@ That provenance file is automatically read and inventoried by Microsoft systems
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
1. Go to ekg/intervaltreerepository on GitHub.
|
||||
1. Go to the ekg/intervaltree repository on GitHub.
|
||||
2. Take the file IntervalTree.h wholesale and drop it into the directory here.
|
||||
3. Don't change anything about it.
|
||||
4. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in the same directory as this readme.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/ekg/intervaltree",
|
||||
"commitHash": "b90527f9e6d51cd36ecbb50429e4524d3a418ea5"
|
||||
"commitHash": "aa5937755000f1cd007402d03b6f7ce4427c5d21"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/kimwalisch/libpopcnt",
|
||||
"commitHash": "043a99fba31121a70bcb2f589faa17f534ae6085"
|
||||
"commitHash": "c49987e90e56191c399cab881ab87b5daecc9b8e"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@
|
||||
<ProjectGuid>{96274800-9574-423E-892A-909FBE2AC8BE}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>EchoCon</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.22000.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22000.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.22621.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
||||
@@ -136,12 +136,12 @@
|
||||
<!-- **END VC LIBS HACK** -->
|
||||
|
||||
<!-- This is required to get the package dependency in the AppXManifest. -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
|
||||
|
||||
@@ -12,19 +12,12 @@ using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
|
||||
namespace xaml = ::winrt::Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::SampleApp::implementation
|
||||
{
|
||||
App::App()
|
||||
{
|
||||
// This is the same trick that Initialize() is about to use to figure out whether we're coming
|
||||
// from a UWP context or from a Win32 context
|
||||
// See https://github.com/windows-toolkit/Microsoft.Toolkit.Win32/blob/52611c57d89554f357f281d0c79036426a7d9257/Microsoft.Toolkit.Win32.UI.XamlApplication/XamlApplication.cpp#L42
|
||||
const auto dispatcherQueue = ::winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
|
||||
if (dispatcherQueue)
|
||||
{
|
||||
_isUwp = true;
|
||||
}
|
||||
|
||||
Initialize();
|
||||
|
||||
// Disable XAML's automatic backplating of text when in High Contrast
|
||||
@@ -33,6 +26,44 @@ namespace winrt::SampleApp::implementation
|
||||
HighContrastAdjustment(::winrt::Windows::UI::Xaml::ApplicationHighContrastAdjustment::None);
|
||||
}
|
||||
|
||||
void App::Initialize()
|
||||
{
|
||||
const auto dispatcherQueue = winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
|
||||
if (!dispatcherQueue)
|
||||
{
|
||||
_windowsXamlManager = xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread();
|
||||
}
|
||||
else
|
||||
{
|
||||
_isUwp = true;
|
||||
}
|
||||
}
|
||||
|
||||
void App::Close()
|
||||
{
|
||||
if (_bIsClosed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_bIsClosed = true;
|
||||
|
||||
if (_windowsXamlManager)
|
||||
{
|
||||
_windowsXamlManager.Close();
|
||||
}
|
||||
_windowsXamlManager = nullptr;
|
||||
|
||||
Exit();
|
||||
{
|
||||
MSG msg = {};
|
||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
|
||||
{
|
||||
::DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SampleAppLogic App::Logic()
|
||||
{
|
||||
static SampleAppLogic logic;
|
||||
|
||||
@@ -12,12 +12,22 @@ namespace winrt::SampleApp::implementation
|
||||
{
|
||||
public:
|
||||
App();
|
||||
void Initialize();
|
||||
void Close();
|
||||
void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs const&);
|
||||
|
||||
bool IsDisposed() const
|
||||
{
|
||||
return _bIsClosed;
|
||||
}
|
||||
|
||||
SampleApp::SampleAppLogic Logic();
|
||||
|
||||
private:
|
||||
bool _isUwp = false;
|
||||
winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager _windowsXamlManager = nullptr;
|
||||
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::UI::Xaml::Markup::IXamlMetadataProvider> _providers = winrt::single_threaded_vector<Windows::UI::Xaml::Markup::IXamlMetadataProvider>();
|
||||
bool _bIsClosed = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,12 @@ namespace SampleApp
|
||||
{
|
||||
// ADD ARBITRARY APP LOGIC TO SampleAppLogic.idl, NOT HERE.
|
||||
// This is for XAML platform setup only.
|
||||
[default_interface] runtimeclass App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
|
||||
[default_interface] runtimeclass App : Windows.UI.Xaml.Application, Windows.Foundation.IClosable
|
||||
{
|
||||
App();
|
||||
|
||||
SampleAppLogic Logic { get; };
|
||||
|
||||
Boolean IsDisposed { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,19 @@
|
||||
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information.
|
||||
-->
|
||||
<Toolkit:XamlApplication x:Class="SampleApp.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:TA="using:SampleApp"
|
||||
xmlns:Toolkit="using:Microsoft.Toolkit.Win32.UI.XamlHost"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:SampleApp"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Application x:Class="SampleApp.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:TA="using:SampleApp"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:SampleApp"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<!--
|
||||
If you want to prove this works, then add `RequestedTheme="Light"` to
|
||||
the properties on the XamlApplication
|
||||
-->
|
||||
<Toolkit:XamlApplication.Resources>
|
||||
<Application.Resources>
|
||||
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
@@ -67,5 +66,5 @@
|
||||
</ResourceDictionary>
|
||||
|
||||
|
||||
</Toolkit:XamlApplication.Resources>
|
||||
</Toolkit:XamlApplication>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
@@ -128,6 +127,16 @@
|
||||
<Private>false</Private>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
</Reference>
|
||||
|
||||
<Reference Include="$(WindowsSDK_MetadataPathVersioned)\Windows.UI.Xaml.Hosting.HostingContract\*\*.winmd">
|
||||
<WinMDFile>true</WinMDFile>
|
||||
<CopyLocal>false</CopyLocal>
|
||||
<ReferenceGrouping>$(TargetPlatformMoniker)</ReferenceGrouping>
|
||||
<ReferenceGroupingDisplayName>$(TargetPlatformDisplayName)</ReferenceGroupingDisplayName>
|
||||
<ResolvedFrom>CppWinRTImplicitlyExpandTargetPlatform</ResolvedFrom>
|
||||
<IsSystemReference>True</IsSystemReference>
|
||||
</Reference>
|
||||
|
||||
</ItemGroup>
|
||||
<!-- ====================== Compiler & Linker Flags ===================== -->
|
||||
<ItemDefinitionGroup>
|
||||
@@ -147,18 +156,18 @@
|
||||
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
By default, the PRI file will contain resource paths beginning with the
|
||||
project name. Since we enabled XBF embedding, this *also* includes App.xbf.
|
||||
Well, App.xbf is hardcoded by the framework to be found at the resource ROOT.
|
||||
Well, App.xbf is hard-coded by the framework to be found at the resource ROOT.
|
||||
To make that happen, we have to disable the prepending of the project name
|
||||
to the App xaml files.
|
||||
-->
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
@@ -45,7 +44,7 @@
|
||||
</ItemGroup>
|
||||
<!-- ========================= Project References ======================== -->
|
||||
<ItemGroup>
|
||||
<!-- Reference SampleAppLib here, so we can use it's App.winmd as
|
||||
<!-- Reference SampleAppLib here, so we can use its App.winmd as
|
||||
our App.winmd. This didn't work correctly in VS2017, you'd need to
|
||||
manually reference the lib -->
|
||||
<ProjectReference Include="$(OpenConsoleDir)scratch\ScratchIslandApp\SampleApp\SampleAppLib.vcxproj">
|
||||
@@ -91,12 +90,12 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.1" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include "winrt/Windows.UI.Xaml.Markup.h"
|
||||
#include "winrt/Windows.UI.ViewManagement.h"
|
||||
|
||||
#include <winrt/Microsoft.Toolkit.Win32.UI.XamlHost.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
Particularly tricky is Microsoft.Terminal.Core.winmd. That winmd doesn't
|
||||
have its own DLL (it doesn't have any activatable classes, only structs and
|
||||
interfaces). However, it too is necessary for Terminal.Control to be able to
|
||||
marshall the Core types across the boundary.
|
||||
marshal the Core types across the boundary.
|
||||
-->
|
||||
<Reference Include="Microsoft.Terminal.Core">
|
||||
<HintPath>$(OpenConsoleCommonOutDir)TerminalCore\Microsoft.Terminal.Core.winmd</HintPath>
|
||||
@@ -144,12 +144,12 @@
|
||||
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!-- Override GetPackagingOutputs to roll up all our dependencies.
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.1" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
#include "MidiAudio.hpp"
|
||||
#include "../terminal/parser/stateMachine.hpp"
|
||||
|
||||
#include <dsound.h>
|
||||
|
||||
#pragma comment(lib, "dxguid.lib")
|
||||
#pragma comment(lib, "dsound.lib")
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -18,66 +13,48 @@ using namespace std::chrono_literals;
|
||||
constexpr auto WAVE_SIZE = 16u;
|
||||
constexpr auto WAVE_DATA = std::array<byte, WAVE_SIZE>{ 128, 159, 191, 223, 255, 223, 191, 159, 128, 96, 64, 32, 0, 32, 64, 96 };
|
||||
|
||||
MidiAudio::MidiAudio(HWND windowHandle)
|
||||
void MidiAudio::_initialize(HWND windowHandle) noexcept
|
||||
{
|
||||
if (SUCCEEDED(DirectSoundCreate8(nullptr, &_directSound, nullptr)))
|
||||
_hwnd = windowHandle;
|
||||
_directSoundModule.reset(LoadLibraryExW(L"dsound.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
|
||||
if (_directSoundModule)
|
||||
{
|
||||
if (SUCCEEDED(_directSound->SetCooperativeLevel(windowHandle, DSSCL_NORMAL)))
|
||||
if (const auto createFunction = GetProcAddressByFunctionDeclaration(_directSoundModule.get(), DirectSoundCreate8))
|
||||
{
|
||||
_createBuffers();
|
||||
if (SUCCEEDED(createFunction(nullptr, &_directSound, nullptr)))
|
||||
{
|
||||
if (SUCCEEDED(_directSound->SetCooperativeLevel(windowHandle, DSSCL_NORMAL)))
|
||||
{
|
||||
_createBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MidiAudio::~MidiAudio() noexcept
|
||||
void MidiAudio::BeginSkip() noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
#pragma warning(suppress : 26447)
|
||||
// We acquire the lock here so the class isn't destroyed while in use.
|
||||
// If this throws, we'll catch it, so the C26447 warning is bogus.
|
||||
const auto lock = std::unique_lock{ _inUseMutex };
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// If the lock fails, we'll just have to live with the consequences.
|
||||
}
|
||||
_skip.SetEvent();
|
||||
}
|
||||
|
||||
void MidiAudio::Initialize()
|
||||
void MidiAudio::EndSkip() noexcept
|
||||
{
|
||||
_shutdownFuture = _shutdownPromise.get_future();
|
||||
_skip.ResetEvent();
|
||||
}
|
||||
|
||||
void MidiAudio::Shutdown()
|
||||
{
|
||||
// Once the shutdown promise is set, any note that is playing will stop
|
||||
// immediately, and the Unlock call will exit the thread ASAP.
|
||||
_shutdownPromise.set_value();
|
||||
}
|
||||
|
||||
void MidiAudio::Lock()
|
||||
{
|
||||
_inUseMutex.lock();
|
||||
}
|
||||
|
||||
void MidiAudio::Unlock()
|
||||
{
|
||||
// We need to check the shutdown status before releasing the mutex,
|
||||
// because after that the class could be destroyed.
|
||||
const auto shutdownStatus = _shutdownFuture.wait_for(0s);
|
||||
_inUseMutex.unlock();
|
||||
// If the wait didn't timeout, that means the shutdown promise was set,
|
||||
// so we need to exit the thread ASAP by throwing an exception.
|
||||
if (shutdownStatus != std::future_status::timeout)
|
||||
{
|
||||
throw Microsoft::Console::VirtualTerminal::StateMachine::ShutdownException{};
|
||||
}
|
||||
}
|
||||
|
||||
void MidiAudio::PlayNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) noexcept
|
||||
void MidiAudio::PlayNote(HWND windowHandle, const int noteNumber, const int velocity, const std::chrono::milliseconds duration) noexcept
|
||||
try
|
||||
{
|
||||
if (_skip.is_signaled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_hwnd != windowHandle)
|
||||
{
|
||||
_initialize(windowHandle);
|
||||
}
|
||||
|
||||
const auto& buffer = _buffers.at(_activeBufferIndex);
|
||||
if (velocity && buffer)
|
||||
{
|
||||
@@ -100,10 +77,10 @@ try
|
||||
buffer->SetCurrentPosition((_lastBufferPosition + 12) % WAVE_SIZE);
|
||||
}
|
||||
|
||||
// By waiting on the shutdown future with the duration of the note, we'll
|
||||
// either be paused for the appropriate amount of time, or we'll break out
|
||||
// of the wait early if we've been shutdown.
|
||||
_shutdownFuture.wait_for(duration);
|
||||
// By waiting on the skip event with a maximum duration of the note, we'll
|
||||
// either be paused for the appropriate amount of time, or we'll break out early
|
||||
// because BeginSkip() was called. This happens for Ctrl+C or during shutdown.
|
||||
_skip.wait(::base::saturated_cast<DWORD>(duration.count()));
|
||||
|
||||
if (velocity && buffer)
|
||||
{
|
||||
|
||||
@@ -12,8 +12,6 @@ Abstract:
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
|
||||
struct IDirectSound8;
|
||||
struct IDirectSoundBuffer;
|
||||
@@ -21,26 +19,20 @@ struct IDirectSoundBuffer;
|
||||
class MidiAudio
|
||||
{
|
||||
public:
|
||||
MidiAudio(HWND windowHandle);
|
||||
MidiAudio(const MidiAudio&) = delete;
|
||||
MidiAudio(MidiAudio&&) = delete;
|
||||
MidiAudio& operator=(const MidiAudio&) = delete;
|
||||
MidiAudio& operator=(MidiAudio&&) = delete;
|
||||
~MidiAudio() noexcept;
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
void PlayNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) noexcept;
|
||||
void BeginSkip() noexcept;
|
||||
void EndSkip() noexcept;
|
||||
void PlayNote(HWND windowHandle, const int noteNumber, const int velocity, const std::chrono::milliseconds duration) noexcept;
|
||||
|
||||
private:
|
||||
void _initialize(HWND windowHandle) noexcept;
|
||||
void _createBuffers() noexcept;
|
||||
|
||||
Microsoft::WRL::ComPtr<IDirectSound8> _directSound;
|
||||
std::array<Microsoft::WRL::ComPtr<IDirectSoundBuffer>, 2> _buffers;
|
||||
wil::slim_event_manual_reset _skip;
|
||||
|
||||
HWND _hwnd = nullptr;
|
||||
wil::unique_hmodule _directSoundModule;
|
||||
wil::com_ptr<IDirectSound8> _directSound;
|
||||
std::array<wil::com_ptr<IDirectSoundBuffer>, 2> _buffers;
|
||||
size_t _activeBufferIndex = 0;
|
||||
DWORD _lastBufferPosition = 0;
|
||||
std::promise<void> _shutdownPromise;
|
||||
std::future<void> _shutdownFuture;
|
||||
std::mutex _inUseMutex;
|
||||
};
|
||||
|
||||
3
src/audio/midi/lib/sources.dep
Normal file
3
src/audio/midi/lib/sources.dep
Normal file
@@ -0,0 +1,3 @@
|
||||
BUILD_PASS1_CONSUMES= \
|
||||
onecore\windows\vcpkg|PASS1 \
|
||||
|
||||
@@ -25,7 +25,9 @@ Abstract:
|
||||
#endif
|
||||
|
||||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#include <mmeapi.h>
|
||||
#include <dsound.h>
|
||||
|
||||
// clang-format on
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#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 til::CoordType width, const TextAttribute attr) :
|
||||
_data(gsl::narrow_cast<uint16_t>(width), attr) {}
|
||||
|
||||
// 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)
|
||||
{
|
||||
_data.replace(0, _data.size(), 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.
|
||||
// If the new size is smaller, the runs are cut off to fit.
|
||||
// Arguments:
|
||||
// - oldWidth - The original width of the row.
|
||||
// - newWidth - The new width of the row.
|
||||
// Return Value:
|
||||
// - <none>, throws exceptions on failures.
|
||||
void ATTR_ROW::Resize(const til::CoordType newWidth)
|
||||
{
|
||||
_data.resize_trailing_extent(gsl::narrow<uint16_t>(newWidth));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - returns a copy of the TextAttribute at the specified column
|
||||
// Arguments:
|
||||
// - column - the column to get the attribute for
|
||||
// Return Value:
|
||||
// - the text attribute at column
|
||||
// Note:
|
||||
// - will throw on error
|
||||
TextAttribute ATTR_ROW::GetAttrByColumn(const til::CoordType column) const
|
||||
{
|
||||
return _data.at(gsl::narrow<uint16_t>(column));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Finds the hyperlink IDs present in this row and returns them
|
||||
// Return value:
|
||||
// - The hyperlink IDs present in this row
|
||||
std::vector<uint16_t> ATTR_ROW::GetHyperlinks() const
|
||||
{
|
||||
std::vector<uint16_t> ids;
|
||||
for (const auto& run : _data.runs())
|
||||
{
|
||||
if (run.value.IsHyperlink())
|
||||
{
|
||||
ids.emplace_back(run.value.GetHyperlinkId());
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Sets the attributes (colors) of all character positions from the given position through the end of the row.
|
||||
// Arguments:
|
||||
// - iStart - Starting index position within the row
|
||||
// - attr - Attribute (color) to fill remaining characters with
|
||||
// Return Value:
|
||||
// - <none>
|
||||
bool ATTR_ROW::SetAttrToEnd(const til::CoordType beginIndex, const TextAttribute attr)
|
||||
{
|
||||
_data.replace(gsl::narrow<uint16_t>(beginIndex), _data.size(), attr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Replaces all runs in the row with the given toBeReplacedAttr with the new
|
||||
// attribute replaceWith.
|
||||
// Arguments:
|
||||
// - toBeReplacedAttr - the attribute to replace in this row.
|
||||
// - replaceWith - the new value for the matching runs' attributes.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith)
|
||||
{
|
||||
_data.replace_values(toBeReplacedAttr, replaceWith);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Takes an attribute, and merges it into this row from beginIndex (inclusive) to endIndex (exclusive).
|
||||
// - For example, if the current row was [{4, BLUE}], the merge arguments were
|
||||
// { beginIndex = 1, endIndex = 3, newAttr = RED }, then the row would modified to be
|
||||
// [{ 1, BLUE}, {2, RED}, {1, BLUE}].
|
||||
// Arguments:
|
||||
// - beginIndex, endIndex: The [beginIndex, endIndex) range that's to be replaced with newAttr.
|
||||
// - newAttr: The attribute to merge into this row.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ATTR_ROW::Replace(const til::CoordType beginIndex, const til::CoordType endIndex, const TextAttribute& newAttr)
|
||||
{
|
||||
_data.replace(gsl::narrow<uint16_t>(beginIndex), gsl::narrow<uint16_t>(endIndex), newAttr);
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::begin() const noexcept
|
||||
{
|
||||
return _data.begin();
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::end() const noexcept
|
||||
{
|
||||
return _data.end();
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::cbegin() const noexcept
|
||||
{
|
||||
return _data.cbegin();
|
||||
}
|
||||
|
||||
ATTR_ROW::const_iterator ATTR_ROW::cend() const noexcept
|
||||
{
|
||||
return _data.cend();
|
||||
}
|
||||
|
||||
bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept
|
||||
{
|
||||
return a._data == b._data;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- AttrRow.hpp
|
||||
|
||||
Abstract:
|
||||
- contains data structure for the attributes of one row of screen buffer
|
||||
|
||||
Author(s):
|
||||
- Michael Niksa (miniksa) 10-Apr-2014
|
||||
- Paul Campbell (paulcam) 10-Apr-2014
|
||||
|
||||
Revision History:
|
||||
- From components of output.h/.c
|
||||
by Therese Stowell (ThereseS) 1990-1991
|
||||
- Pulled into its own file from textBuffer.hpp/cpp (AustDi, 2017)
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "til/rle.h"
|
||||
#include "TextAttribute.hpp"
|
||||
|
||||
class ATTR_ROW final
|
||||
{
|
||||
using rle_vector = til::small_rle<TextAttribute, uint16_t, 1>;
|
||||
|
||||
public:
|
||||
using const_iterator = rle_vector::const_iterator;
|
||||
|
||||
ATTR_ROW(til::CoordType width, TextAttribute attr);
|
||||
|
||||
~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;
|
||||
|
||||
TextAttribute GetAttrByColumn(til::CoordType column) const;
|
||||
std::vector<uint16_t> GetHyperlinks() const;
|
||||
|
||||
bool SetAttrToEnd(til::CoordType beginIndex, TextAttribute attr);
|
||||
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith);
|
||||
void Resize(til::CoordType newWidth);
|
||||
void Replace(til::CoordType beginIndex, til::CoordType endIndex, const TextAttribute& newAttr);
|
||||
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
|
||||
const_iterator cbegin() const noexcept;
|
||||
const_iterator cend() const noexcept;
|
||||
|
||||
friend bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept;
|
||||
friend class ROW;
|
||||
|
||||
private:
|
||||
void Reset(const TextAttribute attr);
|
||||
|
||||
rle_vector _data;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class CommonState;
|
||||
#endif
|
||||
};
|
||||
@@ -1,281 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "CharRow.hpp"
|
||||
#include "unicode.hpp"
|
||||
#include "Row.hpp"
|
||||
|
||||
// Routine Description:
|
||||
// - constructor
|
||||
// Arguments:
|
||||
// - rowWidth - the size (in wchar_t) of the char and attribute rows
|
||||
// - pParent - the parent ROW
|
||||
// Return Value:
|
||||
// - instantiated object
|
||||
// Note: will through if unable to allocate char/attribute buffers
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26447) // small_vector's constructor says it can throw but it should not given how we use it. This suppresses this error for the AuditMode build.
|
||||
CharRow::CharRow(til::CoordType rowWidth, ROW* const pParent) noexcept :
|
||||
_data(rowWidth, value_type()),
|
||||
_pParent{ FAIL_FAST_IF_NULL(pParent) }
|
||||
{
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
// Routine Description:
|
||||
// - gets the size of the row, in glyph cells
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the size of the row
|
||||
til::CoordType CharRow::size() const noexcept
|
||||
{
|
||||
return gsl::narrow_cast<til::CoordType>(_data.size());
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Sets all properties of the CharRowBase to default values
|
||||
// Arguments:
|
||||
// - sRowWidth - The width of the row.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CharRow::Reset() noexcept
|
||||
{
|
||||
for (auto& cell : _data)
|
||||
{
|
||||
cell.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - resizes the width of the CharRowBase
|
||||
// Arguments:
|
||||
// - newSize - the new width of the character and attributes rows
|
||||
// Return Value:
|
||||
// - S_OK on success, otherwise relevant error code
|
||||
[[nodiscard]] HRESULT CharRow::Resize(const til::CoordType newSize) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
const value_type insertVals;
|
||||
_data.resize(newSize, insertVals);
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
typename CharRow::iterator CharRow::begin() noexcept
|
||||
{
|
||||
return _data.begin();
|
||||
}
|
||||
|
||||
typename CharRow::const_iterator CharRow::cbegin() const noexcept
|
||||
{
|
||||
return _data.cbegin();
|
||||
}
|
||||
|
||||
typename CharRow::iterator CharRow::end() noexcept
|
||||
{
|
||||
return _data.end();
|
||||
}
|
||||
|
||||
typename CharRow::const_iterator CharRow::cend() const noexcept
|
||||
{
|
||||
return _data.cend();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Inspects the current internal string to find the left edge of it
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The calculated left boundary of the internal string.
|
||||
til::CoordType CharRow::MeasureLeft() const noexcept
|
||||
{
|
||||
auto it = _data.cbegin();
|
||||
while (it != _data.cend() && it->IsSpace())
|
||||
{
|
||||
++it;
|
||||
}
|
||||
return gsl::narrow_cast<til::CoordType>(it - _data.cbegin());
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Inspects the current internal string to find the right edge of it
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The calculated right boundary of the internal string.
|
||||
til::CoordType CharRow::MeasureRight() const
|
||||
{
|
||||
auto it = _data.crbegin();
|
||||
while (it != _data.crend() && it->IsSpace())
|
||||
{
|
||||
++it;
|
||||
}
|
||||
return gsl::narrow_cast<til::CoordType>(_data.crend() - it);
|
||||
}
|
||||
|
||||
void CharRow::ClearCell(const til::CoordType column)
|
||||
{
|
||||
_data.at(column).Reset();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Tells you whether or not this row contains any valid text.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - True if there is valid text in this row. False otherwise.
|
||||
bool CharRow::ContainsText() const noexcept
|
||||
{
|
||||
for (const auto& cell : _data)
|
||||
{
|
||||
if (!cell.IsSpace())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - gets the attribute at the specified column
|
||||
// Arguments:
|
||||
// - column - the column to get the attribute for
|
||||
// Return Value:
|
||||
// - the attribute
|
||||
// Note: will throw exception if column is out of bounds
|
||||
const DbcsAttribute& CharRow::DbcsAttrAt(const til::CoordType column) const
|
||||
{
|
||||
return _data.at(column).DbcsAttr();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - gets the attribute at the specified column
|
||||
// Arguments:
|
||||
// - column - the column to get the attribute for
|
||||
// Return Value:
|
||||
// - the attribute
|
||||
// Note: will throw exception if column is out of bounds
|
||||
DbcsAttribute& CharRow::DbcsAttrAt(const til::CoordType column)
|
||||
{
|
||||
return _data.at(column).DbcsAttr();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - resets text data at column
|
||||
// Arguments:
|
||||
// - column - column index to clear text data from
|
||||
// Return Value:
|
||||
// - <none>
|
||||
// Note: will throw exception if column is out of bounds
|
||||
void CharRow::ClearGlyph(const til::CoordType column)
|
||||
{
|
||||
_data.at(column).EraseChars();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - returns text data at column as a const reference.
|
||||
// Arguments:
|
||||
// - column - column to get text data for
|
||||
// Return Value:
|
||||
// - text data at column
|
||||
// - Note: will throw exception if column is out of bounds
|
||||
const CharRow::reference CharRow::GlyphAt(const til::CoordType column) const
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, column < 0 || column >= gsl::narrow_cast<til::CoordType>(_data.size()));
|
||||
return { const_cast<CharRow&>(*this), column };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - returns text data at column as a reference.
|
||||
// Arguments:
|
||||
// - column - column to get text data for
|
||||
// Return Value:
|
||||
// - text data at column
|
||||
// - Note: will throw exception if column is out of bounds
|
||||
CharRow::reference CharRow::GlyphAt(const til::CoordType column)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, column < 0 || column >= gsl::narrow_cast<til::CoordType>(_data.size()));
|
||||
return { *this, column };
|
||||
}
|
||||
|
||||
std::wstring CharRow::GetText() const
|
||||
{
|
||||
std::wstring wstr;
|
||||
wstr.reserve(_data.size());
|
||||
|
||||
for (til::CoordType i = 0; i < gsl::narrow_cast<til::CoordType>(_data.size()); ++i)
|
||||
{
|
||||
const auto glyph = GlyphAt(i);
|
||||
if (!DbcsAttrAt(i).IsTrailing())
|
||||
{
|
||||
for (const auto wch : glyph)
|
||||
{
|
||||
wstr.push_back(wch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return wstr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - get delimiter class for a position in the char row
|
||||
// - used for double click selection and uia word navigation
|
||||
// Arguments:
|
||||
// - column: column to get text data for
|
||||
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
|
||||
// Return Value:
|
||||
// - the delimiter class for the given char
|
||||
const DelimiterClass CharRow::DelimiterClassAt(const til::CoordType column, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, column < 0 || column >= gsl::narrow_cast<til::CoordType>(_data.size()));
|
||||
|
||||
const auto glyph = *GlyphAt(column).begin();
|
||||
if (glyph <= UNICODE_SPACE)
|
||||
{
|
||||
return DelimiterClass::ControlChar;
|
||||
}
|
||||
else if (wordDelimiters.find(glyph) != std::wstring_view::npos)
|
||||
{
|
||||
return DelimiterClass::DelimiterChar;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DelimiterClass::RegularChar;
|
||||
}
|
||||
}
|
||||
|
||||
UnicodeStorage& CharRow::GetUnicodeStorage() noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
}
|
||||
|
||||
const UnicodeStorage& CharRow::GetUnicodeStorage() const noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - calculates the key used by the given column of the char row to store glyph data in UnicodeStorage
|
||||
// Arguments:
|
||||
// - column - the column to generate the key for
|
||||
// Return Value:
|
||||
// - the til::point key for data access from UnicodeStorage for the column
|
||||
til::point CharRow::GetStorageKey(const til::CoordType column) const noexcept
|
||||
{
|
||||
return { column, _pParent->GetId() };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Updates the pointer to the parent row (which might change if we shuffle the rows around)
|
||||
// Arguments:
|
||||
// - pParent - Pointer to the parent row
|
||||
void CharRow::UpdateParent(ROW* const pParent)
|
||||
{
|
||||
_pParent = FAIL_FAST_IF_NULL(pParent);
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- CharRow.hpp
|
||||
|
||||
Abstract:
|
||||
- contains data structure for UCS2 encoded character data of a row
|
||||
|
||||
Author(s):
|
||||
- Michael Niksa (miniksa) 10-Apr-2014
|
||||
- Paul Campbell (paulcam) 10-Apr-2014
|
||||
|
||||
Revision History:
|
||||
- From components of output.h/.c
|
||||
by Therese Stowell (ThereseS) 1990-1991
|
||||
- Pulled into its own file from textBuffer.hpp/cpp (AustDi, 2017)
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <til/small_vector.h>
|
||||
|
||||
#include "DbcsAttribute.hpp"
|
||||
#include "CharRowCellReference.hpp"
|
||||
#include "CharRowCell.hpp"
|
||||
#include "UnicodeStorage.hpp"
|
||||
|
||||
class ROW;
|
||||
|
||||
enum class DelimiterClass
|
||||
{
|
||||
ControlChar,
|
||||
DelimiterChar,
|
||||
RegularChar
|
||||
};
|
||||
|
||||
// the characters of one row of screen buffer
|
||||
// we keep the following values so that we don't write
|
||||
// more pixels to the screen than we have to:
|
||||
// left is initialized to screenbuffer width. right is
|
||||
// initialized to zero.
|
||||
//
|
||||
// [ foo.bar 12-12-61 ]
|
||||
// ^ ^ ^ ^
|
||||
// | | | |
|
||||
// Chars Left Right end of Chars buffer
|
||||
class CharRow final
|
||||
{
|
||||
public:
|
||||
using glyph_type = wchar_t;
|
||||
using value_type = CharRowCell;
|
||||
using iterator = til::small_vector<value_type, 120>::iterator;
|
||||
using const_iterator = til::small_vector<value_type, 120>::const_iterator;
|
||||
using const_reverse_iterator = til::small_vector<value_type, 120>::const_reverse_iterator;
|
||||
using reference = CharRowCellReference;
|
||||
|
||||
CharRow(til::CoordType rowWidth, ROW* const pParent) noexcept;
|
||||
|
||||
til::CoordType size() const noexcept;
|
||||
[[nodiscard]] HRESULT Resize(const til::CoordType newSize) noexcept;
|
||||
til::CoordType MeasureLeft() const noexcept;
|
||||
til::CoordType MeasureRight() const;
|
||||
bool ContainsText() const noexcept;
|
||||
const DbcsAttribute& DbcsAttrAt(const til::CoordType column) const;
|
||||
DbcsAttribute& DbcsAttrAt(const til::CoordType column);
|
||||
void ClearGlyph(const til::CoordType column);
|
||||
|
||||
const DelimiterClass DelimiterClassAt(const til::CoordType column, const std::wstring_view wordDelimiters) const;
|
||||
|
||||
// working with glyphs
|
||||
const reference GlyphAt(const til::CoordType column) const;
|
||||
reference GlyphAt(const til::CoordType column);
|
||||
|
||||
// iterators
|
||||
iterator begin() noexcept;
|
||||
const_iterator cbegin() const noexcept;
|
||||
const_iterator begin() const noexcept { return cbegin(); }
|
||||
|
||||
iterator end() noexcept;
|
||||
const_iterator cend() const noexcept;
|
||||
const_iterator end() const noexcept { return cend(); }
|
||||
|
||||
UnicodeStorage& GetUnicodeStorage() noexcept;
|
||||
const UnicodeStorage& GetUnicodeStorage() const noexcept;
|
||||
til::point GetStorageKey(const til::CoordType column) const noexcept;
|
||||
|
||||
void UpdateParent(ROW* const pParent);
|
||||
|
||||
friend CharRowCellReference;
|
||||
friend class ROW;
|
||||
|
||||
private:
|
||||
void Reset() noexcept;
|
||||
void ClearCell(const til::CoordType column);
|
||||
std::wstring GetText() const;
|
||||
|
||||
protected:
|
||||
// storage for glyph data and dbcs attributes
|
||||
til::small_vector<value_type, 120> _data;
|
||||
|
||||
// ROW that this CharRow belongs to
|
||||
ROW* _pParent;
|
||||
};
|
||||
|
||||
template<typename InputIt1, typename InputIt2>
|
||||
void OverwriteColumns(InputIt1 startChars, InputIt1 endChars, InputIt2 startAttrs, CharRow::iterator outIt)
|
||||
{
|
||||
std::transform(startChars,
|
||||
endChars,
|
||||
startAttrs,
|
||||
outIt,
|
||||
[](const wchar_t wch, const DbcsAttribute attr) {
|
||||
return CharRow::value_type{ wch, attr };
|
||||
});
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
#include "precomp.h"
|
||||
|
||||
#include "CharRowCell.hpp"
|
||||
#include "unicode.hpp"
|
||||
|
||||
// default glyph value, used for resetting the character data portion of a cell
|
||||
static constexpr wchar_t DefaultValue = UNICODE_SPACE;
|
||||
|
||||
// Routine Description:
|
||||
// - "erases" the glyph. really sets it back to the default "empty" value
|
||||
void CharRowCell::EraseChars() noexcept
|
||||
{
|
||||
if (_attr.IsGlyphStored())
|
||||
{
|
||||
_attr.SetGlyphStored(false);
|
||||
}
|
||||
_wch = DefaultValue;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - resets this object back to the defaults it would have from the default constructor
|
||||
void CharRowCell::Reset() noexcept
|
||||
{
|
||||
_attr.Reset();
|
||||
_wch = DefaultValue;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - checks if cell contains a space glyph
|
||||
// Return Value:
|
||||
// - true if cell contains a space glyph, false otherwise
|
||||
bool CharRowCell::IsSpace() const noexcept
|
||||
{
|
||||
return !_attr.IsGlyphStored() && _wch == UNICODE_SPACE;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Access the DbcsAttribute for the cell
|
||||
// Return Value:
|
||||
// - ref to the cells' DbcsAttribute
|
||||
DbcsAttribute& CharRowCell::DbcsAttr() noexcept
|
||||
{
|
||||
return _attr;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Access the DbcsAttribute for the cell
|
||||
// Return Value:
|
||||
// - ref to the cells' DbcsAttribute
|
||||
const DbcsAttribute& CharRowCell::DbcsAttr() const noexcept
|
||||
{
|
||||
return _attr;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Access the cell's wchar field. this does not access any char data through UnicodeStorage.
|
||||
// Return Value:
|
||||
// - the cell's wchar field
|
||||
wchar_t& CharRowCell::Char() noexcept
|
||||
{
|
||||
return _wch;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Access the cell's wchar field. this does not access any char data through UnicodeStorage.
|
||||
// Return Value:
|
||||
// - the cell's wchar field
|
||||
const wchar_t& CharRowCell::Char() const noexcept
|
||||
{
|
||||
return _wch;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- CharRowCell.hpp
|
||||
|
||||
Abstract:
|
||||
- data structure for one cell of a char row. contains the char data for one
|
||||
coordinate position in the output buffer (leading/trailing information and
|
||||
the char itself.
|
||||
|
||||
Author(s):
|
||||
- Austin Diviness (AustDi) 02-May-2018
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DbcsAttribute.hpp"
|
||||
#include "unicode.hpp"
|
||||
|
||||
#if (defined(_M_IX86) || defined(_M_AMD64))
|
||||
// currently CharRowCell's fields use 3 bytes of memory, leaving the 4th byte in unused. this leads
|
||||
// to a rather large amount of useless memory allocated. so instead, pack CharRowCell by bytes instead of words.
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
|
||||
class CharRowCell final
|
||||
{
|
||||
public:
|
||||
CharRowCell() noexcept = default;
|
||||
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept
|
||||
:
|
||||
_wch(wch),
|
||||
_attr(attr)
|
||||
{
|
||||
}
|
||||
|
||||
void EraseChars() noexcept;
|
||||
void Reset() noexcept;
|
||||
|
||||
bool IsSpace() const noexcept;
|
||||
|
||||
DbcsAttribute& DbcsAttr() noexcept;
|
||||
const DbcsAttribute& DbcsAttr() const noexcept;
|
||||
|
||||
wchar_t& Char() noexcept;
|
||||
const wchar_t& Char() const noexcept;
|
||||
|
||||
friend constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept;
|
||||
|
||||
private:
|
||||
wchar_t _wch{ UNICODE_SPACE };
|
||||
DbcsAttribute _attr{};
|
||||
};
|
||||
|
||||
#if (defined(_M_IX86) || defined(_M_AMD64))
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept
|
||||
{
|
||||
return (a._wch == b._wch &&
|
||||
a._attr == b._attr);
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "UnicodeStorage.hpp"
|
||||
#include "CharRow.hpp"
|
||||
|
||||
// Routine Description:
|
||||
// - assignment operator. will store extended glyph data in a separate storage location
|
||||
// Arguments:
|
||||
// - chars - the glyph data to store
|
||||
void CharRowCellReference::operator=(const std::wstring_view chars)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, chars.empty());
|
||||
if (chars.size() == 1)
|
||||
{
|
||||
_cellData().Char() = chars.front();
|
||||
_cellData().DbcsAttr().SetGlyphStored(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& storage = _parent.GetUnicodeStorage();
|
||||
const auto key = _parent.GetStorageKey(_index);
|
||||
storage.StoreGlyph(key, { chars.cbegin(), chars.cend() });
|
||||
_cellData().DbcsAttr().SetGlyphStored(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - implicit conversion to vector<wchar_t> operator.
|
||||
// Return Value:
|
||||
// - std::vector<wchar_t> of the glyph data in the referenced cell
|
||||
CharRowCellReference::operator std::wstring_view() const
|
||||
{
|
||||
return _glyphData();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - The CharRowCell this object "references"
|
||||
// Return Value:
|
||||
// - ref to the CharRowCell
|
||||
CharRowCell& CharRowCellReference::_cellData()
|
||||
{
|
||||
return _parent._data.at(_index);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - The CharRowCell this object "references"
|
||||
// Return Value:
|
||||
// - ref to the CharRowCell
|
||||
const CharRowCell& CharRowCellReference::_cellData() const
|
||||
{
|
||||
return _parent._data.at(_index);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - the glyph data of the referenced cell
|
||||
// Return Value:
|
||||
// - the glyph data
|
||||
std::wstring_view CharRowCellReference::_glyphData() const
|
||||
{
|
||||
if (_cellData().DbcsAttr().IsGlyphStored())
|
||||
{
|
||||
const auto& text = _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index));
|
||||
|
||||
return { text.data(), text.size() };
|
||||
}
|
||||
else
|
||||
{
|
||||
return { &_cellData().Char(), 1 };
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - gets read-only iterator to the beginning of the glyph data
|
||||
// Return Value:
|
||||
// - iterator of the glyph data
|
||||
CharRowCellReference::const_iterator CharRowCellReference::begin() const
|
||||
{
|
||||
if (_cellData().DbcsAttr().IsGlyphStored())
|
||||
{
|
||||
return _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index)).data();
|
||||
}
|
||||
else
|
||||
{
|
||||
return &_cellData().Char();
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - get read-only iterator to the end of the glyph data
|
||||
// Return Value:
|
||||
// - end iterator of the glyph data
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26481)
|
||||
// TODO GH 2672: eliminate using pointers raw as begin/end markers in this class
|
||||
CharRowCellReference::const_iterator CharRowCellReference::end() const
|
||||
{
|
||||
if (_cellData().DbcsAttr().IsGlyphStored())
|
||||
{
|
||||
const auto& chars = _parent.GetUnicodeStorage().GetText(_parent.GetStorageKey(_index));
|
||||
return chars.data() + chars.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
return &_cellData().Char() + 1;
|
||||
}
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph)
|
||||
{
|
||||
const auto& dbcsAttr = ref._cellData().DbcsAttr();
|
||||
if (glyph.size() == 1 && dbcsAttr.IsGlyphStored())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (glyph.size() > 1 && !dbcsAttr.IsGlyphStored())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (glyph.size() == 1 && !dbcsAttr.IsGlyphStored())
|
||||
{
|
||||
return ref._cellData().Char() == glyph.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& chars = ref._parent.GetUnicodeStorage().GetText(ref._parent.GetStorageKey(ref._index));
|
||||
return chars == glyph;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref)
|
||||
{
|
||||
return ref == glyph;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- CharRowCellReference.hpp
|
||||
|
||||
Abstract:
|
||||
- reference class for the glyph data of a char row cell
|
||||
|
||||
Author(s):
|
||||
- Austin Diviness (AustDi) 02-May-2018
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DbcsAttribute.hpp"
|
||||
#include "CharRowCell.hpp"
|
||||
#include <utility>
|
||||
|
||||
class CharRow;
|
||||
|
||||
class CharRowCellReference final
|
||||
{
|
||||
public:
|
||||
using const_iterator = const wchar_t*;
|
||||
|
||||
CharRowCellReference(CharRow& parent, const til::CoordType index) noexcept :
|
||||
_parent{ parent },
|
||||
_index{ index }
|
||||
{
|
||||
}
|
||||
|
||||
~CharRowCellReference() = default;
|
||||
CharRowCellReference(const CharRowCellReference&) noexcept = default;
|
||||
CharRowCellReference(CharRowCellReference&&) noexcept = default;
|
||||
|
||||
void operator=(const CharRowCellReference&) = delete;
|
||||
void operator=(CharRowCellReference&&) = delete;
|
||||
|
||||
void operator=(const std::wstring_view chars);
|
||||
operator std::wstring_view() const;
|
||||
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
|
||||
friend bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph);
|
||||
friend bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref);
|
||||
|
||||
private:
|
||||
// what char row the object belongs to
|
||||
CharRow& _parent;
|
||||
// the index of the cell in the parent char row
|
||||
til::CoordType _index;
|
||||
|
||||
CharRowCell& _cellData();
|
||||
const CharRowCell& _cellData() const;
|
||||
|
||||
std::wstring_view _glyphData() const;
|
||||
};
|
||||
|
||||
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph);
|
||||
bool operator==(const std::vector<wchar_t>& glyph, const CharRowCellReference& ref);
|
||||
@@ -16,129 +16,22 @@ Revision History:
|
||||
|
||||
#pragma once
|
||||
|
||||
class DbcsAttribute final
|
||||
enum class DbcsAttribute : uint8_t
|
||||
{
|
||||
public:
|
||||
enum class Attribute : BYTE
|
||||
{
|
||||
Single = 0x00,
|
||||
Leading = 0x01,
|
||||
Trailing = 0x02
|
||||
};
|
||||
|
||||
DbcsAttribute() noexcept :
|
||||
_attribute{ Attribute::Single },
|
||||
_glyphStored{ false }
|
||||
{
|
||||
}
|
||||
|
||||
DbcsAttribute(const Attribute attribute) noexcept :
|
||||
_attribute{ attribute },
|
||||
_glyphStored{ false }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool IsSingle() const noexcept
|
||||
{
|
||||
return _attribute == Attribute::Single;
|
||||
}
|
||||
|
||||
constexpr bool IsLeading() const noexcept
|
||||
{
|
||||
return _attribute == Attribute::Leading;
|
||||
}
|
||||
|
||||
constexpr bool IsTrailing() const noexcept
|
||||
{
|
||||
return _attribute == Attribute::Trailing;
|
||||
}
|
||||
|
||||
constexpr bool IsDbcs() const noexcept
|
||||
{
|
||||
return IsLeading() || IsTrailing();
|
||||
}
|
||||
|
||||
constexpr bool IsGlyphStored() const noexcept
|
||||
{
|
||||
return _glyphStored;
|
||||
}
|
||||
|
||||
void SetGlyphStored(const bool stored) noexcept
|
||||
{
|
||||
_glyphStored = stored;
|
||||
}
|
||||
|
||||
void SetSingle() noexcept
|
||||
{
|
||||
_attribute = Attribute::Single;
|
||||
}
|
||||
|
||||
void SetLeading() noexcept
|
||||
{
|
||||
_attribute = Attribute::Leading;
|
||||
}
|
||||
|
||||
void SetTrailing() noexcept
|
||||
{
|
||||
_attribute = Attribute::Trailing;
|
||||
}
|
||||
|
||||
void Reset() noexcept
|
||||
{
|
||||
SetSingle();
|
||||
SetGlyphStored(false);
|
||||
}
|
||||
|
||||
WORD GeneratePublicApiAttributeFormat() const noexcept
|
||||
{
|
||||
WORD publicAttribute = 0;
|
||||
if (IsLeading())
|
||||
{
|
||||
WI_SetFlag(publicAttribute, COMMON_LVB_LEADING_BYTE);
|
||||
}
|
||||
if (IsTrailing())
|
||||
{
|
||||
WI_SetFlag(publicAttribute, COMMON_LVB_TRAILING_BYTE);
|
||||
}
|
||||
return publicAttribute;
|
||||
}
|
||||
|
||||
static DbcsAttribute FromPublicApiAttributeFormat(WORD publicAttribute)
|
||||
{
|
||||
// it's not valid to be both a leading and trailing byte
|
||||
if (WI_AreAllFlagsSet(publicAttribute, COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE))
|
||||
{
|
||||
THROW_HR(E_INVALIDARG);
|
||||
}
|
||||
|
||||
DbcsAttribute attr;
|
||||
if (WI_IsFlagSet(publicAttribute, COMMON_LVB_LEADING_BYTE))
|
||||
{
|
||||
attr.SetLeading();
|
||||
}
|
||||
else if (WI_IsFlagSet(publicAttribute, COMMON_LVB_TRAILING_BYTE))
|
||||
{
|
||||
attr.SetTrailing();
|
||||
}
|
||||
return attr;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(const DbcsAttribute& a, const DbcsAttribute& b) noexcept;
|
||||
|
||||
private:
|
||||
Attribute _attribute : 2;
|
||||
bool _glyphStored : 1;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
#endif
|
||||
Single,
|
||||
Leading,
|
||||
Trailing,
|
||||
};
|
||||
|
||||
constexpr bool operator==(const DbcsAttribute& a, const DbcsAttribute& b) noexcept
|
||||
constexpr WORD GeneratePublicApiAttributeFormat(DbcsAttribute attribute) noexcept
|
||||
{
|
||||
return a._attribute == b._attribute;
|
||||
switch (attribute)
|
||||
{
|
||||
case DbcsAttribute::Leading:
|
||||
return COMMON_LVB_LEADING_BYTE;
|
||||
case DbcsAttribute::Trailing:
|
||||
return COMMON_LVB_TRAILING_BYTE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(sizeof(DbcsAttribute) == sizeof(BYTE), "DbcsAttribute should be one byte big. if this changes then it needs "
|
||||
"either an implicit conversion to a BYTE or an update to all places "
|
||||
"that assume it's a byte big");
|
||||
|
||||
@@ -13,7 +13,7 @@ Abstract:
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class LineRendition
|
||||
enum class LineRendition : uint8_t
|
||||
{
|
||||
SingleWidth,
|
||||
DoubleWidth,
|
||||
@@ -25,19 +25,19 @@ constexpr til::inclusive_rect ScreenToBufferLine(const til::inclusive_rect& line
|
||||
{
|
||||
// Use shift right to quickly divide the Left and Right by 2 for double width lines.
|
||||
const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
|
||||
return { line.Left >> scale, line.Top, line.Right >> scale, line.Bottom };
|
||||
return { line.left >> scale, line.top, line.right >> scale, line.bottom };
|
||||
}
|
||||
|
||||
constexpr til::point ScreenToBufferLine(const til::point& line, const LineRendition lineRendition)
|
||||
{
|
||||
// Use shift right to quickly divide the Left and Right by 2 for double width lines.
|
||||
const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
|
||||
return { line.X >> scale, line.Y };
|
||||
return { line.x >> scale, line.y };
|
||||
}
|
||||
|
||||
constexpr til::inclusive_rect BufferToScreenLine(const til::inclusive_rect& line, const LineRendition lineRendition)
|
||||
{
|
||||
// Use shift left to quickly multiply the Left and Right by 2 for double width lines.
|
||||
const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
|
||||
return { line.Left << scale, line.Top, (line.Right << scale) + scale, line.Bottom };
|
||||
return { line.left << scale, line.top, (line.right << scale) + scale, line.bottom };
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ private:
|
||||
// basic_string contains a small storage internally so we don't need
|
||||
// to worry about heap allocation for short strings.
|
||||
std::wstring _text;
|
||||
DbcsAttribute _dbcsAttribute;
|
||||
DbcsAttribute _dbcsAttribute = DbcsAttribute::Single;
|
||||
TextAttribute _textAttribute;
|
||||
TextAttributeBehavior _behavior;
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
|
||||
#include "OutputCellIterator.hpp"
|
||||
|
||||
#include <til/unicode.h>
|
||||
|
||||
#include "../../types/inc/convert.hpp"
|
||||
#include "../../types/inc/Utf16Parser.hpp"
|
||||
#include "../../types/inc/GlyphWidth.hpp"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
|
||||
@@ -81,7 +82,7 @@ OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t f
|
||||
// - This is an iterator over a range of text only. No color data will be modified as the text is inserted.
|
||||
// Arguments:
|
||||
// - utf16Text - UTF-16 text range
|
||||
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text) :
|
||||
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text) noexcept :
|
||||
_mode(Mode::LooseTextOnly),
|
||||
_currentView(s_GenerateView(utf16Text)),
|
||||
_run(utf16Text),
|
||||
@@ -97,7 +98,7 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text) :
|
||||
// Arguments:
|
||||
// - utf16Text - UTF-16 text range
|
||||
// - attribute - Color to apply over the entire range
|
||||
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute& attribute, const size_t fillLimit) :
|
||||
OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute& attribute, const size_t fillLimit) noexcept :
|
||||
_mode(Mode::Loose),
|
||||
_currentView(s_GenerateView(utf16Text, attribute)),
|
||||
_run(utf16Text),
|
||||
@@ -240,13 +241,10 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
{
|
||||
if (!_TryMoveTrailing())
|
||||
{
|
||||
if (_currentView.DbcsAttr().IsTrailing())
|
||||
if (_currentView.DbcsAttr() == DbcsAttribute::Trailing)
|
||||
{
|
||||
auto dbcsAttr = _currentView.DbcsAttr();
|
||||
dbcsAttr.SetLeading();
|
||||
|
||||
_currentView = OutputCellView(_currentView.Chars(),
|
||||
dbcsAttr,
|
||||
DbcsAttribute::Leading,
|
||||
_currentView.TextAttr(),
|
||||
_currentView.TextAttrBehavior());
|
||||
}
|
||||
@@ -336,13 +334,10 @@ const OutputCellView* OutputCellIterator::operator->() const noexcept
|
||||
// - False if this wasn't applicable and the caller should update the view.
|
||||
bool OutputCellIterator::_TryMoveTrailing() noexcept
|
||||
{
|
||||
if (_currentView.DbcsAttr().IsLeading())
|
||||
if (_currentView.DbcsAttr() == DbcsAttribute::Leading)
|
||||
{
|
||||
auto dbcsAttr = _currentView.DbcsAttr();
|
||||
dbcsAttr.SetTrailing();
|
||||
|
||||
_currentView = OutputCellView(_currentView.Chars(),
|
||||
dbcsAttr,
|
||||
DbcsAttribute::Trailing,
|
||||
_currentView.TextAttr(),
|
||||
_currentView.TextAttrBehavior());
|
||||
return true;
|
||||
@@ -362,7 +357,7 @@ bool OutputCellIterator::_TryMoveTrailing() noexcept
|
||||
// - view - View representing characters corresponding to a single glyph
|
||||
// Return Value:
|
||||
// - Object representing the view into this cell
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view)
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view) noexcept
|
||||
{
|
||||
return s_GenerateView(view, InvalidTextAttribute, TextAttributeBehavior::Current);
|
||||
}
|
||||
@@ -377,8 +372,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view)
|
||||
// - attr - Color attributes to apply to the text
|
||||
// Return Value:
|
||||
// - Object representing the view into this cell
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
|
||||
const TextAttribute attr)
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view, const TextAttribute attr) noexcept
|
||||
{
|
||||
return s_GenerateView(view, attr, TextAttributeBehavior::Stored);
|
||||
}
|
||||
@@ -394,17 +388,10 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
|
||||
// - behavior - Behavior of the given text attribute (used when writing)
|
||||
// Return Value:
|
||||
// - Object representing the view into this cell
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
|
||||
const TextAttribute attr,
|
||||
const TextAttributeBehavior behavior)
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view, const TextAttribute attr, const TextAttributeBehavior behavior) noexcept
|
||||
{
|
||||
const auto glyph = Utf16Parser::ParseNext(view);
|
||||
DbcsAttribute dbcsAttr;
|
||||
if (IsGlyphFullWidth(glyph))
|
||||
{
|
||||
dbcsAttr.SetLeading();
|
||||
}
|
||||
|
||||
const auto glyph = til::utf16_next(view);
|
||||
const auto dbcsAttr = IsGlyphFullWidth(glyph) ? DbcsAttribute::Leading : DbcsAttribute::Single;
|
||||
return OutputCellView(glyph, dbcsAttr, attr, behavior);
|
||||
}
|
||||
|
||||
@@ -420,13 +407,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) noexcept
|
||||
{
|
||||
const auto glyph = std::wstring_view(&wch, 1);
|
||||
|
||||
DbcsAttribute dbcsAttr;
|
||||
if (IsGlyphFullWidth(wch))
|
||||
{
|
||||
dbcsAttr.SetLeading();
|
||||
}
|
||||
|
||||
const auto dbcsAttr = IsGlyphFullWidth(wch) ? DbcsAttribute::Leading : DbcsAttribute::Single;
|
||||
return OutputCellView(glyph, dbcsAttr, InvalidTextAttribute, TextAttributeBehavior::Current);
|
||||
}
|
||||
|
||||
@@ -457,13 +438,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) noe
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept
|
||||
{
|
||||
const auto glyph = std::wstring_view(&wch, 1);
|
||||
|
||||
DbcsAttribute dbcsAttr;
|
||||
if (IsGlyphFullWidth(wch))
|
||||
{
|
||||
dbcsAttr.SetLeading();
|
||||
}
|
||||
|
||||
const auto dbcsAttr = IsGlyphFullWidth(wch) ? DbcsAttribute::Leading : DbcsAttribute::Single;
|
||||
return OutputCellView(glyph, dbcsAttr, attr, TextAttributeBehavior::Stored);
|
||||
}
|
||||
|
||||
@@ -498,14 +473,14 @@ OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo) noe
|
||||
{
|
||||
const auto glyph = std::wstring_view(&charInfo.Char.UnicodeChar, 1);
|
||||
|
||||
DbcsAttribute dbcsAttr;
|
||||
DbcsAttribute dbcsAttr = DbcsAttribute::Single;
|
||||
if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_LEADING_BYTE))
|
||||
{
|
||||
dbcsAttr.SetLeading();
|
||||
dbcsAttr = DbcsAttribute::Leading;
|
||||
}
|
||||
else if (WI_IsFlagSet(charInfo.Attributes, COMMON_LVB_TRAILING_BYTE))
|
||||
{
|
||||
dbcsAttr.SetTrailing();
|
||||
dbcsAttr = DbcsAttribute::Trailing;
|
||||
}
|
||||
|
||||
const TextAttribute textAttr(charInfo.Attributes);
|
||||
|
||||
@@ -37,8 +37,8 @@ public:
|
||||
OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const std::wstring_view utf16Text);
|
||||
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute& attribute, const size_t fillLimit = 0);
|
||||
OutputCellIterator(const std::wstring_view utf16Text) noexcept;
|
||||
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute& attribute, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const gsl::span<const WORD> legacyAttributes) noexcept;
|
||||
OutputCellIterator(const gsl::span<const CHAR_INFO> charInfos) noexcept;
|
||||
OutputCellIterator(const gsl::span<const OutputCell> cells);
|
||||
@@ -100,15 +100,9 @@ private:
|
||||
|
||||
bool _TryMoveTrailing() noexcept;
|
||||
|
||||
static OutputCellView s_GenerateView(const std::wstring_view view);
|
||||
|
||||
static OutputCellView s_GenerateView(const std::wstring_view view,
|
||||
const TextAttribute attr);
|
||||
|
||||
static OutputCellView s_GenerateView(const std::wstring_view view,
|
||||
const TextAttribute attr,
|
||||
const TextAttributeBehavior behavior);
|
||||
|
||||
static OutputCellView s_GenerateView(const std::wstring_view view) noexcept;
|
||||
static OutputCellView s_GenerateView(const std::wstring_view view, const TextAttribute attr) noexcept;
|
||||
static OutputCellView s_GenerateView(const std::wstring_view view, const TextAttribute attr, const TextAttributeBehavior behavior) noexcept;
|
||||
static OutputCellView s_GenerateView(const wchar_t& wch) noexcept;
|
||||
static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept;
|
||||
static OutputCellView s_GenerateView(const TextAttribute& attr) noexcept;
|
||||
|
||||
@@ -40,20 +40,7 @@ OutputCellView::OutputCellView(const std::wstring_view view,
|
||||
// - Count of column cells on the screen
|
||||
til::CoordType OutputCellView::Columns() const noexcept
|
||||
{
|
||||
if (DbcsAttr().IsSingle())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (DbcsAttr().IsLeading())
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (DbcsAttr().IsTrailing())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return DbcsAttr() == DbcsAttribute::Leading ? 2 : 1;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
|
||||
private:
|
||||
std::wstring_view _view;
|
||||
DbcsAttribute _dbcsAttr;
|
||||
DbcsAttribute _dbcsAttr = DbcsAttribute::Single;
|
||||
TextAttribute _textAttr;
|
||||
TextAttributeBehavior _behavior;
|
||||
};
|
||||
|
||||
@@ -3,29 +3,129 @@
|
||||
|
||||
#include "precomp.h"
|
||||
#include "Row.hpp"
|
||||
#include "CharRow.hpp"
|
||||
|
||||
#include "textBuffer.hpp"
|
||||
#include "../types/inc/convert.hpp"
|
||||
|
||||
// The STL is missing a std::iota_n analogue for std::iota, so I made my own.
|
||||
template<typename OutIt, typename Diff, typename T>
|
||||
constexpr OutIt iota_n(OutIt dest, Diff count, T val)
|
||||
{
|
||||
for (; count; --count, ++dest, ++val)
|
||||
{
|
||||
*dest = val;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// ROW::ReplaceCharacters needs to calculate `val + count` after
|
||||
// calling iota_n() and this function achieves both things at once.
|
||||
template<typename OutIt, typename Diff, typename T>
|
||||
constexpr OutIt iota_n_mut(OutIt dest, Diff count, T& val)
|
||||
{
|
||||
for (; count; --count, ++dest, ++val)
|
||||
{
|
||||
*dest = val;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Same as std::fill, but purpose-built for very small `last - first`
|
||||
// where a trivial loop outperforms vectorization.
|
||||
template<typename FwdIt, typename T>
|
||||
constexpr FwdIt fill_small(FwdIt first, FwdIt last, const T val)
|
||||
{
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
*first = val;
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
// Same as std::fill_n, but purpose-built for very small `count`
|
||||
// where a trivial loop outperforms vectorization.
|
||||
template<typename OutIt, typename Diff, typename T>
|
||||
constexpr OutIt fill_n_small(OutIt dest, Diff count, const T val)
|
||||
{
|
||||
for (; count; --count, ++dest)
|
||||
{
|
||||
*dest = val;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Same as std::copy_n, but purpose-built for very short `count`
|
||||
// where a trivial loop outperforms vectorization.
|
||||
template<typename InIt, typename Diff, typename OutIt>
|
||||
constexpr OutIt copy_n_small(InIt first, Diff count, OutIt dest)
|
||||
{
|
||||
for (; count; --count, ++dest, ++first)
|
||||
{
|
||||
*dest = *first;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - constructor
|
||||
// Arguments:
|
||||
// - rowId - the row index in the text buffer
|
||||
// - rowWidth - the width of the row, cell elements
|
||||
// - fillAttribute - the default text attribute
|
||||
// - pParent - the text buffer that this row belongs to
|
||||
// Return Value:
|
||||
// - constructed object
|
||||
ROW::ROW(const til::CoordType rowId, const til::CoordType rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) :
|
||||
_id{ rowId },
|
||||
_rowWidth{ rowWidth },
|
||||
_charRow{ rowWidth, this },
|
||||
_attrRow{ rowWidth, fillAttribute },
|
||||
_lineRendition{ LineRendition::SingleWidth },
|
||||
_wrapForced{ false },
|
||||
_doubleBytePadded{ false },
|
||||
_pParent{ pParent }
|
||||
ROW::ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute) :
|
||||
_charsBuffer{ charsBuffer },
|
||||
_chars{ charsBuffer, rowWidth },
|
||||
_charOffsets{ charOffsetsBuffer, ::base::strict_cast<size_t>(rowWidth) + 1u },
|
||||
_attr{ rowWidth, fillAttribute },
|
||||
_columnCount{ rowWidth }
|
||||
{
|
||||
if (_chars.data())
|
||||
{
|
||||
_init();
|
||||
}
|
||||
}
|
||||
|
||||
void swap(ROW& lhs, ROW& rhs) noexcept
|
||||
{
|
||||
std::swap(lhs._charsBuffer, rhs._charsBuffer);
|
||||
std::swap(lhs._charsHeap, rhs._charsHeap);
|
||||
std::swap(lhs._chars, rhs._chars);
|
||||
std::swap(lhs._charOffsets, rhs._charOffsets);
|
||||
std::swap(lhs._attr, rhs._attr);
|
||||
std::swap(lhs._columnCount, rhs._columnCount);
|
||||
std::swap(lhs._lineRendition, rhs._lineRendition);
|
||||
std::swap(lhs._wrapForced, rhs._wrapForced);
|
||||
std::swap(lhs._doubleBytePadded, rhs._doubleBytePadded);
|
||||
}
|
||||
|
||||
void ROW::SetWrapForced(const bool wrap) noexcept
|
||||
{
|
||||
_wrapForced = wrap;
|
||||
}
|
||||
|
||||
bool ROW::WasWrapForced() const noexcept
|
||||
{
|
||||
return _wrapForced;
|
||||
}
|
||||
|
||||
void ROW::SetDoubleBytePadded(const bool doubleBytePadded) noexcept
|
||||
{
|
||||
_doubleBytePadded = doubleBytePadded;
|
||||
}
|
||||
|
||||
bool ROW::WasDoubleBytePadded() const noexcept
|
||||
{
|
||||
return _doubleBytePadded;
|
||||
}
|
||||
|
||||
void ROW::SetLineRendition(const LineRendition lineRendition) noexcept
|
||||
{
|
||||
_lineRendition = lineRendition;
|
||||
}
|
||||
|
||||
LineRendition ROW::GetLineRendition() const noexcept
|
||||
{
|
||||
return _lineRendition;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -34,42 +134,99 @@ ROW::ROW(const til::CoordType rowId, const til::CoordType rowWidth, const TextAt
|
||||
// - Attr - The default attribute (color) to fill
|
||||
// Return Value:
|
||||
// - <none>
|
||||
bool ROW::Reset(const TextAttribute Attr)
|
||||
void ROW::Reset(const TextAttribute& attr)
|
||||
{
|
||||
_charsHeap.reset();
|
||||
_chars = { _charsBuffer, _columnCount };
|
||||
_attr = { _columnCount, attr };
|
||||
_lineRendition = LineRendition::SingleWidth;
|
||||
_wrapForced = false;
|
||||
_doubleBytePadded = false;
|
||||
_charRow.Reset();
|
||||
try
|
||||
{
|
||||
_attrRow.Reset(Attr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
_init();
|
||||
}
|
||||
|
||||
void ROW::_init() noexcept
|
||||
{
|
||||
std::fill_n(_chars.begin(), _columnCount, UNICODE_SPACE);
|
||||
std::iota(_charOffsets.begin(), _charOffsets.end(), uint16_t{ 0 });
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - resizes ROW to new width
|
||||
// Arguments:
|
||||
// - width - the new width, in cells
|
||||
// Return Value:
|
||||
// - S_OK if successful, otherwise relevant error
|
||||
[[nodiscard]] HRESULT ROW::Resize(const til::CoordType width)
|
||||
// - charsBuffer - a new backing buffer to use for _charsBuffer
|
||||
// - charOffsetsBuffer - a new backing buffer to use for _charOffsets
|
||||
// - rowWidth - the new width, in cells
|
||||
// - fillAttribute - the attribute to use for any newly added, trailing cells
|
||||
void ROW::Resize(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute)
|
||||
{
|
||||
RETURN_IF_FAILED(_charRow.Resize(width));
|
||||
try
|
||||
// A default-constructed ROW has no cols/chars to copy.
|
||||
// It can be detected by the lack of a _charsBuffer (among others).
|
||||
//
|
||||
// Otherwise, this block figures out how much we can copy into the new `rowWidth`.
|
||||
uint16_t colsToCopy = 0;
|
||||
uint16_t charsToCopy = 0;
|
||||
if (_charsBuffer)
|
||||
{
|
||||
_attrRow.Resize(width);
|
||||
colsToCopy = std::min(rowWidth, _columnCount);
|
||||
// Safety: colsToCopy is [0, _columnCount].
|
||||
charsToCopy = _uncheckedCharOffset(colsToCopy);
|
||||
// Safety: colsToCopy is [0, _columnCount] due to colsToCopy != 0.
|
||||
for (; colsToCopy != 0 && _uncheckedIsTrailer(colsToCopy); --colsToCopy)
|
||||
{
|
||||
}
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
_rowWidth = width;
|
||||
// If we grow the row width, we have to append a bunch of whitespace.
|
||||
// `trailingWhitespace` stores that amount.
|
||||
// Safety: The preceding block left colsToCopy in the range [0, rowWidth].
|
||||
const uint16_t trailingWhitespace = rowWidth - colsToCopy;
|
||||
|
||||
return S_OK;
|
||||
// Allocate memory for the new `_chars` array.
|
||||
// Use the provided charsBuffer if possible, otherwise allocate a `_charsHeap`.
|
||||
std::unique_ptr<wchar_t[]> charsHeap;
|
||||
std::span chars{ charsBuffer, rowWidth };
|
||||
const std::span charOffsets{ charOffsetsBuffer, ::base::strict_cast<size_t>(rowWidth) + 1u };
|
||||
if (const uint16_t charsCapacity = charsToCopy + trailingWhitespace; charsCapacity > rowWidth)
|
||||
{
|
||||
charsHeap = std::make_unique_for_overwrite<wchar_t[]>(charsCapacity);
|
||||
chars = { charsHeap.get(), charsCapacity };
|
||||
}
|
||||
|
||||
// Copy chars and charOffsets over.
|
||||
{
|
||||
const auto it = std::copy_n(_chars.begin(), charsToCopy, chars.begin());
|
||||
std::fill_n(it, trailingWhitespace, L' ');
|
||||
}
|
||||
{
|
||||
const auto it = std::copy_n(_charOffsets.begin(), colsToCopy, charOffsets.begin());
|
||||
// The _charOffsets array is 1 wider than newWidth indicates.
|
||||
// This is because the extra column contains the past-the-end index into _chars.
|
||||
iota_n(it, trailingWhitespace + 1u, charsToCopy);
|
||||
}
|
||||
|
||||
_charsBuffer = charsBuffer;
|
||||
_charsHeap = std::move(charsHeap);
|
||||
_chars = chars;
|
||||
_charOffsets = charOffsets;
|
||||
_columnCount = rowWidth;
|
||||
|
||||
// .resize_trailing_extent() doesn't work if the vector is empty,
|
||||
// since there's no trailing item that could be extended.
|
||||
if (_attr.empty())
|
||||
{
|
||||
_attr = { rowWidth, fillAttribute };
|
||||
}
|
||||
else
|
||||
{
|
||||
_attr.resize_trailing_extent(rowWidth);
|
||||
}
|
||||
}
|
||||
|
||||
void ROW::TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth)
|
||||
{
|
||||
_attr = attr;
|
||||
_attr.resize_trailing_extent(gsl::narrow<uint16_t>(newWidth));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -78,20 +235,10 @@ bool ROW::Reset(const TextAttribute Attr)
|
||||
// - column - 0-indexed column index
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ROW::ClearColumn(const til::CoordType column)
|
||||
void ROW::ClearCell(const til::CoordType column)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, column >= _charRow.size());
|
||||
_charRow.ClearCell(column);
|
||||
}
|
||||
|
||||
UnicodeStorage& ROW::GetUnicodeStorage() noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
}
|
||||
|
||||
const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
static constexpr std::wstring_view space{ L" " };
|
||||
ReplaceCharacters(column, 1, space);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -103,17 +250,17 @@ const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
|
||||
// - limitRight - right inclusive column ID for the last write in this row. (optional, will just write to the end of row if nullopt)
|
||||
// Return Value:
|
||||
// - iterator to first cell that was not written to this row.
|
||||
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType index, const std::optional<bool> wrap, std::optional<til::CoordType> limitRight)
|
||||
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType columnBegin, const std::optional<bool> wrap, std::optional<til::CoordType> limitRight)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, index >= _charRow.size());
|
||||
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= _charRow.size());
|
||||
THROW_HR_IF(E_INVALIDARG, columnBegin >= size());
|
||||
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= size());
|
||||
|
||||
// If we're given a right-side column limit, use it. Otherwise, the write limit is the final column index available in the char row.
|
||||
const auto finalColumnInRow = limitRight.value_or(_charRow.size() - 1);
|
||||
const auto finalColumnInRow = limitRight.value_or(size() - 1);
|
||||
|
||||
auto currentColor = it->TextAttr();
|
||||
uint16_t colorUses = 0;
|
||||
auto colorStarts = gsl::narrow_cast<uint16_t>(index);
|
||||
auto colorStarts = gsl::narrow_cast<uint16_t>(columnBegin);
|
||||
auto currentIndex = colorStarts;
|
||||
|
||||
while (it && currentIndex <= finalColumnInRow)
|
||||
@@ -131,7 +278,7 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType i
|
||||
{
|
||||
// Otherwise, commit this color into the run and save off the new one.
|
||||
// Now commit the new color runs into the attr row.
|
||||
_attrRow.Replace(colorStarts, currentIndex, currentColor);
|
||||
_attr.replace(colorStarts, currentIndex, currentColor);
|
||||
currentColor = it->TextAttr();
|
||||
colorUses = 1;
|
||||
colorStarts = currentIndex;
|
||||
@@ -141,31 +288,47 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType i
|
||||
// Fill the text if the behavior isn't set to saying there's only a color stored in this iterator.
|
||||
if (it->TextAttrBehavior() != TextAttributeBehavior::StoredOnly)
|
||||
{
|
||||
const auto fillingFirstColumn = currentIndex == 0;
|
||||
const auto fillingLastColumn = currentIndex == finalColumnInRow;
|
||||
const auto attr = it->DbcsAttr();
|
||||
const auto& chars = it->Chars();
|
||||
|
||||
// TODO: MSFT: 19452170 - We need to ensure when writing any trailing byte that the one to the left
|
||||
// is a matching leading byte. Likewise, if we're writing a leading byte, we need to make sure we still have space in this loop
|
||||
// for the trailing byte coming up before writing it.
|
||||
|
||||
// If we're trying to fill the first cell with a trailing byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll advance the index and try again with this value on the next round through the loop.
|
||||
if (currentIndex == 0 && it->DbcsAttr().IsTrailing())
|
||||
switch (attr)
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
}
|
||||
// If we're trying to fill the last cell with a leading byte, pad it out instead by clearing it.
|
||||
// Don't increment iterator. We'll exit because we couldn't write a lead at the end of a line.
|
||||
else if (fillingLastColumn && it->DbcsAttr().IsLeading())
|
||||
{
|
||||
_charRow.ClearCell(currentIndex);
|
||||
SetDoubleBytePadded(true);
|
||||
}
|
||||
// Otherwise, copy the data given and increment the iterator.
|
||||
else
|
||||
{
|
||||
_charRow.DbcsAttrAt(currentIndex) = it->DbcsAttr();
|
||||
_charRow.GlyphAt(currentIndex) = it->Chars();
|
||||
case DbcsAttribute::Leading:
|
||||
if (fillingLastColumn)
|
||||
{
|
||||
// The wide char doesn't fit. Pad with whitespace.
|
||||
// Don't increment the iterator. Instead we'll return from this function and the
|
||||
// caller can call WriteCells() again on the next row with the same iterator position.
|
||||
ClearCell(currentIndex);
|
||||
SetDoubleBytePadded(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReplaceCharacters(currentIndex, 2, chars);
|
||||
++it;
|
||||
}
|
||||
break;
|
||||
case DbcsAttribute::Trailing:
|
||||
// Handling the trailing half of wide chars ensures that we correctly restore
|
||||
// wide characters when a user backs up and restores the viewport via CHAR_INFOs.
|
||||
if (fillingFirstColumn)
|
||||
{
|
||||
// The wide char doesn't fit. Pad with whitespace.
|
||||
// Ignore the character. There's no correct alternative way to handle this situation.
|
||||
ClearCell(currentIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReplaceCharacters(currentIndex - 1, 2, chars);
|
||||
}
|
||||
++it;
|
||||
break;
|
||||
default:
|
||||
ReplaceCharacters(currentIndex, 1, chars);
|
||||
++it;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
|
||||
@@ -191,8 +354,334 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType i
|
||||
// Now commit the final color into the attr row
|
||||
if (colorUses)
|
||||
{
|
||||
_attrRow.Replace(colorStarts, currentIndex, currentColor);
|
||||
_attr.replace(colorStarts, currentIndex, currentColor);
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
bool ROW::SetAttrToEnd(const til::CoordType columnBegin, const TextAttribute attr)
|
||||
{
|
||||
_attr.replace(_clampedColumnInclusive(columnBegin), _attr.size(), attr);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ROW::ReplaceAttributes(const til::CoordType beginIndex, const til::CoordType endIndex, const TextAttribute& newAttr)
|
||||
{
|
||||
_attr.replace(_clampedColumnInclusive(beginIndex), _clampedColumnInclusive(endIndex), newAttr);
|
||||
}
|
||||
|
||||
void ROW::ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars)
|
||||
{
|
||||
const auto colBeg = _clampedUint16(columnBegin);
|
||||
const auto colEnd = _clampedUint16(columnBegin + width);
|
||||
|
||||
if (colBeg >= colEnd || colEnd > _columnCount || chars.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Safety:
|
||||
// * colBeg is now [0, _columnCount)
|
||||
// * colEnd is now (colBeg, _columnCount]
|
||||
|
||||
// Algorithm explanation
|
||||
//
|
||||
// Task:
|
||||
// Replace the characters in cells [colBeg, colEnd) with a single `width`-wide glyph consisting of `chars`.
|
||||
//
|
||||
// Problem:
|
||||
// Imagine that we have the following ROW contents:
|
||||
// "xxyyzz"
|
||||
// xx, yy, zz are 2 cell wide glyphs. We want to insert a 2 cell wide glyph ww at colBeg 1:
|
||||
// ^^
|
||||
// ww
|
||||
// An incorrect result would be:
|
||||
// "xwwyzz"
|
||||
// The half cut off x and y glyph wouldn't make much sense, so we need to fill them with whitespace:
|
||||
// " ww zz"
|
||||
//
|
||||
// Solution:
|
||||
// Given the range we want to replace [colBeg, colEnd), we "extend" it to encompass leading (preceding)
|
||||
// and trailing wide glyphs we partially overwrite resulting in the range [colExtBeg, colExtEnd), where
|
||||
// colExtBeg <= colBeg and colExtEnd >= colEnd. In other words, the to be replaced range has been "extended".
|
||||
// The amount of leading whitespace we need to insert is thus colBeg - colExtBeg
|
||||
// and the amount of trailing whitespace colExtEnd - colEnd.
|
||||
|
||||
// Extend range downwards (leading whitespace)
|
||||
uint16_t colExtBeg = colBeg;
|
||||
// Safety: colExtBeg is [0, _columnCount], because colBeg is.
|
||||
const uint16_t chExtBeg = _uncheckedCharOffset(colExtBeg);
|
||||
// Safety: colExtBeg remains [0, _columnCount] due to colExtBeg != 0.
|
||||
for (; colExtBeg != 0 && _uncheckedIsTrailer(colExtBeg); --colExtBeg)
|
||||
{
|
||||
}
|
||||
|
||||
// Extend range upwards (trailing whitespace)
|
||||
uint16_t colExtEnd = colEnd;
|
||||
// Safety: colExtEnd cannot be incremented past _columnCount, because the last
|
||||
// _charOffset at index _columnCount will never get the CharOffsetsTrailer flag.
|
||||
for (; _uncheckedIsTrailer(colExtEnd); ++colExtEnd)
|
||||
{
|
||||
}
|
||||
// Safety: After the previous loop colExtEnd is [0, _columnCount].
|
||||
const uint16_t chExtEnd = _uncheckedCharOffset(colExtEnd);
|
||||
|
||||
const uint16_t leadingSpaces = colBeg - colExtBeg;
|
||||
const uint16_t trailingSpaces = colExtEnd - colEnd;
|
||||
const size_t chExtEndNew = chars.size() + leadingSpaces + trailingSpaces + chExtBeg;
|
||||
|
||||
if (chExtEndNew != chExtEnd)
|
||||
{
|
||||
_resizeChars(colExtEnd, chExtBeg, chExtEnd, chExtEndNew);
|
||||
}
|
||||
|
||||
// Add leading/trailing whitespace and copy chars
|
||||
{
|
||||
auto it = _chars.begin() + chExtBeg;
|
||||
it = fill_n_small(it, leadingSpaces, L' ');
|
||||
it = copy_n_small(chars.begin(), chars.size(), it);
|
||||
it = fill_n_small(it, trailingSpaces, L' ');
|
||||
}
|
||||
// Update char offsets with leading/trailing whitespace and the chars columns.
|
||||
{
|
||||
auto chPos = chExtBeg;
|
||||
auto it = _charOffsets.begin() + colExtBeg;
|
||||
|
||||
it = iota_n_mut(it, leadingSpaces, chPos);
|
||||
|
||||
*it++ = chPos;
|
||||
it = fill_small(it, _charOffsets.begin() + colEnd, gsl::narrow_cast<uint16_t>(chPos | CharOffsetsTrailer));
|
||||
chPos = gsl::narrow_cast<uint16_t>(chPos + chars.size());
|
||||
|
||||
it = iota_n_mut(it, trailingSpaces, chPos);
|
||||
}
|
||||
}
|
||||
|
||||
// This function represents the slow path of ReplaceCharacters(),
|
||||
// as it reallocates the backing buffer and shifts the char offsets.
|
||||
// The parameters are difficult to explain, but their names are identical to
|
||||
// local variables in ReplaceCharacters() which I've attempted to document there.
|
||||
void ROW::_resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd, size_t chExtEndNew)
|
||||
{
|
||||
const auto diff = chExtEndNew - chExtEnd;
|
||||
const auto currentLength = _charSize();
|
||||
const auto newLength = currentLength + diff;
|
||||
|
||||
if (newLength <= _chars.size())
|
||||
{
|
||||
std::copy_n(_chars.begin() + chExtEnd, currentLength - chExtEnd, _chars.begin() + chExtEndNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto minCapacity = std::min<size_t>(UINT16_MAX, _chars.size() + (_chars.size() >> 1));
|
||||
const auto newCapacity = gsl::narrow<uint16_t>(std::max(newLength, minCapacity));
|
||||
|
||||
auto charsHeap = std::make_unique_for_overwrite<wchar_t[]>(newCapacity);
|
||||
const std::span chars{ charsHeap.get(), newCapacity };
|
||||
|
||||
std::copy_n(_chars.begin(), chExtBeg, chars.begin());
|
||||
std::copy_n(_chars.begin() + chExtEnd, currentLength - chExtEnd, chars.begin() + chExtEndNew);
|
||||
|
||||
_charsHeap = std::move(charsHeap);
|
||||
_chars = chars;
|
||||
}
|
||||
|
||||
auto it = _charOffsets.begin() + colExtEnd;
|
||||
const auto end = _charOffsets.end();
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
*it = gsl::narrow_cast<uint16_t>(*it + diff);
|
||||
}
|
||||
}
|
||||
|
||||
const til::small_rle<TextAttribute, uint16_t, 1>& ROW::Attributes() const noexcept
|
||||
{
|
||||
return _attr;
|
||||
}
|
||||
|
||||
TextAttribute ROW::GetAttrByColumn(const til::CoordType column) const
|
||||
{
|
||||
return _attr.at(_clampedUint16(column));
|
||||
}
|
||||
|
||||
std::vector<uint16_t> ROW::GetHyperlinks() const
|
||||
{
|
||||
std::vector<uint16_t> ids;
|
||||
for (const auto& run : _attr.runs())
|
||||
{
|
||||
if (run.value.IsHyperlink())
|
||||
{
|
||||
ids.emplace_back(run.value.GetHyperlinkId());
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
uint16_t ROW::size() const noexcept
|
||||
{
|
||||
return _columnCount;
|
||||
}
|
||||
|
||||
til::CoordType ROW::MeasureLeft() const noexcept
|
||||
{
|
||||
const auto text = GetText();
|
||||
const auto beg = text.begin();
|
||||
const auto end = text.end();
|
||||
auto it = beg;
|
||||
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (*it != L' ')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return gsl::narrow_cast<til::CoordType>(it - beg);
|
||||
}
|
||||
|
||||
til::CoordType ROW::MeasureRight() const noexcept
|
||||
{
|
||||
const auto text = GetText();
|
||||
const auto beg = text.begin();
|
||||
const auto end = text.end();
|
||||
auto it = end;
|
||||
|
||||
for (; it != beg; --it)
|
||||
{
|
||||
// it[-1] is safe as `it` is always greater than `beg` (loop invariant).
|
||||
if (til::at(it, -1) != L' ')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We're supposed to return the measurement in cells and not characters
|
||||
// and therefore simply calculating `it - beg` would be wrong.
|
||||
//
|
||||
// An example: The row is 10 cells wide and `it` points to the second character.
|
||||
// `it - beg` would return 1, but it's possible it's actually 1 wide glyph and 8 whitespace.
|
||||
return gsl::narrow_cast<til::CoordType>(_columnCount - (end - it));
|
||||
}
|
||||
|
||||
bool ROW::ContainsText() const noexcept
|
||||
{
|
||||
const auto text = GetText();
|
||||
const auto beg = text.begin();
|
||||
const auto end = text.end();
|
||||
auto it = beg;
|
||||
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (*it != L' ')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring_view ROW::GlyphAt(til::CoordType column) const noexcept
|
||||
{
|
||||
auto col = _clampedColumn(column);
|
||||
|
||||
// Safety: col is [0, _columnCount).
|
||||
const auto beg = _uncheckedCharOffset(col);
|
||||
// Safety: col cannot be incremented past _columnCount, because the last
|
||||
// _charOffset at index _columnCount will never get the CharOffsetsTrailer flag.
|
||||
while (_uncheckedIsTrailer(++col))
|
||||
{
|
||||
}
|
||||
// Safety: col is now (0, _columnCount].
|
||||
const auto end = _uncheckedCharOffset(col);
|
||||
|
||||
return { _chars.begin() + beg, _chars.begin() + end };
|
||||
}
|
||||
|
||||
DbcsAttribute ROW::DbcsAttrAt(til::CoordType column) const noexcept
|
||||
{
|
||||
const auto col = _clampedColumn(column);
|
||||
|
||||
auto attr = DbcsAttribute::Single;
|
||||
// Safety: col is [0, _columnCount).
|
||||
if (_uncheckedIsTrailer(col))
|
||||
{
|
||||
attr = DbcsAttribute::Trailing;
|
||||
}
|
||||
// Safety: col+1 is [1, _columnCount].
|
||||
else if (_uncheckedIsTrailer(::base::strict_cast<size_t>(col) + 1u))
|
||||
{
|
||||
attr = DbcsAttribute::Leading;
|
||||
}
|
||||
|
||||
return { attr };
|
||||
}
|
||||
|
||||
std::wstring_view ROW::GetText() const noexcept
|
||||
{
|
||||
return { _chars.data(), _charSize() };
|
||||
}
|
||||
|
||||
DelimiterClass ROW::DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept
|
||||
{
|
||||
const auto col = _clampedColumn(column);
|
||||
// Safety: col is [0, _columnCount).
|
||||
const auto glyph = _uncheckedChar(_uncheckedCharOffset(col));
|
||||
|
||||
if (glyph <= L' ')
|
||||
{
|
||||
return DelimiterClass::ControlChar;
|
||||
}
|
||||
else if (wordDelimiters.find(glyph) != std::wstring_view::npos)
|
||||
{
|
||||
return DelimiterClass::DelimiterChar;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DelimiterClass::RegularChar;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr uint16_t ROW::_clampedUint16(T v) noexcept
|
||||
{
|
||||
return static_cast<uint16_t>(std::max(T{ 0 }, std::min(T{ 65535 }, v)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr uint16_t ROW::_clampedColumn(T v) const noexcept
|
||||
{
|
||||
return static_cast<uint16_t>(std::max(T{ 0 }, std::min<T>(_columnCount - 1u, v)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr uint16_t ROW::_clampedColumnInclusive(T v) const noexcept
|
||||
{
|
||||
return static_cast<uint16_t>(std::max(T{ 0 }, std::min<T>(_columnCount, v)));
|
||||
}
|
||||
|
||||
// Safety: off must be [0, _charSize()].
|
||||
wchar_t ROW::_uncheckedChar(size_t off) const noexcept
|
||||
{
|
||||
return til::at(_chars, off);
|
||||
}
|
||||
|
||||
uint16_t ROW::_charSize() const noexcept
|
||||
{
|
||||
// Safety: _charOffsets is an array of `_columnCount + 1` entries.
|
||||
return til::at(_charOffsets, _columnCount);
|
||||
}
|
||||
|
||||
// Safety: col must be [0, _columnCount].
|
||||
uint16_t ROW::_uncheckedCharOffset(size_t col) const noexcept
|
||||
{
|
||||
return til::at(_charOffsets, col) & CharOffsetsMask;
|
||||
}
|
||||
|
||||
// Safety: col must be [0, _columnCount].
|
||||
bool ROW::_uncheckedIsTrailer(size_t col) const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(til::at(_charOffsets, col), CharOffsetsTrailer);
|
||||
}
|
||||
|
||||
@@ -20,50 +20,68 @@ Revision History:
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AttrRow.hpp"
|
||||
#include <span>
|
||||
|
||||
#include <til/rle.h>
|
||||
|
||||
#include "LineRendition.hpp"
|
||||
#include "OutputCell.hpp"
|
||||
#include "OutputCellIterator.hpp"
|
||||
#include "CharRow.hpp"
|
||||
#include "UnicodeStorage.hpp"
|
||||
|
||||
class TextBuffer;
|
||||
|
||||
enum class DelimiterClass
|
||||
{
|
||||
ControlChar,
|
||||
DelimiterChar,
|
||||
RegularChar
|
||||
};
|
||||
|
||||
class ROW final
|
||||
{
|
||||
public:
|
||||
ROW(const til::CoordType rowId, const til::CoordType rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent);
|
||||
ROW() = default;
|
||||
ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute);
|
||||
|
||||
til::CoordType size() const noexcept { return _rowWidth; }
|
||||
ROW(const ROW& other) = delete;
|
||||
ROW& operator=(const ROW& other) = delete;
|
||||
|
||||
void SetWrapForced(const bool wrap) noexcept { _wrapForced = wrap; }
|
||||
bool WasWrapForced() const noexcept { return _wrapForced; }
|
||||
explicit ROW(ROW&& other) = default;
|
||||
ROW& operator=(ROW&& other) = default;
|
||||
|
||||
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept { _doubleBytePadded = doubleBytePadded; }
|
||||
bool WasDoubleBytePadded() const noexcept { return _doubleBytePadded; }
|
||||
friend void swap(ROW& lhs, ROW& rhs) noexcept;
|
||||
|
||||
const CharRow& GetCharRow() const noexcept { return _charRow; }
|
||||
CharRow& GetCharRow() noexcept { return _charRow; }
|
||||
void SetWrapForced(const bool wrap) noexcept;
|
||||
bool WasWrapForced() const noexcept;
|
||||
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept;
|
||||
bool WasDoubleBytePadded() const noexcept;
|
||||
void SetLineRendition(const LineRendition lineRendition) noexcept;
|
||||
LineRendition GetLineRendition() const noexcept;
|
||||
|
||||
const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
|
||||
ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
|
||||
void Reset(const TextAttribute& attr);
|
||||
void Resize(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute);
|
||||
void TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth);
|
||||
|
||||
LineRendition GetLineRendition() const noexcept { return _lineRendition; }
|
||||
void SetLineRendition(const LineRendition lineRendition) noexcept { _lineRendition = lineRendition; }
|
||||
void ClearCell(til::CoordType column);
|
||||
OutputCellIterator WriteCells(OutputCellIterator it, til::CoordType columnBegin, std::optional<bool> wrap = std::nullopt, std::optional<til::CoordType> limitRight = std::nullopt);
|
||||
bool SetAttrToEnd(til::CoordType columnBegin, TextAttribute attr);
|
||||
void ReplaceAttributes(til::CoordType beginIndex, til::CoordType endIndex, const TextAttribute& newAttr);
|
||||
void ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars);
|
||||
|
||||
til::CoordType GetId() const noexcept { return _id; }
|
||||
void SetId(const til::CoordType id) noexcept { _id = id; }
|
||||
const til::small_rle<TextAttribute, uint16_t, 1>& Attributes() const noexcept;
|
||||
TextAttribute GetAttrByColumn(til::CoordType column) const;
|
||||
std::vector<uint16_t> GetHyperlinks() const;
|
||||
uint16_t size() const noexcept;
|
||||
til::CoordType MeasureLeft() const noexcept;
|
||||
til::CoordType MeasureRight() const noexcept;
|
||||
bool ContainsText() const noexcept;
|
||||
std::wstring_view GlyphAt(til::CoordType column) const noexcept;
|
||||
DbcsAttribute DbcsAttrAt(til::CoordType column) const noexcept;
|
||||
std::wstring_view GetText() const noexcept;
|
||||
DelimiterClass DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept;
|
||||
|
||||
bool Reset(const TextAttribute Attr);
|
||||
[[nodiscard]] HRESULT Resize(const til::CoordType width);
|
||||
|
||||
void ClearColumn(const til::CoordType column);
|
||||
std::wstring GetText() const { return _charRow.GetText(); }
|
||||
|
||||
UnicodeStorage& GetUnicodeStorage() noexcept;
|
||||
const UnicodeStorage& GetUnicodeStorage() const noexcept;
|
||||
|
||||
OutputCellIterator WriteCells(OutputCellIterator it, const til::CoordType index, const std::optional<bool> wrap = std::nullopt, std::optional<til::CoordType> limitRight = std::nullopt);
|
||||
auto AttrBegin() const noexcept { return _attr.begin(); }
|
||||
auto AttrEnd() const noexcept { return _attr.end(); }
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend constexpr bool operator==(const ROW& a, const ROW& b) noexcept;
|
||||
@@ -71,23 +89,84 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
CharRow _charRow;
|
||||
ATTR_ROW _attrRow;
|
||||
LineRendition _lineRendition;
|
||||
til::CoordType _id;
|
||||
til::CoordType _rowWidth;
|
||||
// To simplify the detection of wide glyphs, we don't just store the simple character offset as described
|
||||
// for _charOffsets. Instead we use the most significant bit to indicate whether any column is the
|
||||
// trailing half of a wide glyph. This simplifies many implementation details via _uncheckedIsTrailer.
|
||||
static constexpr uint16_t CharOffsetsTrailer = 0x8000;
|
||||
static constexpr uint16_t CharOffsetsMask = 0x7fff;
|
||||
|
||||
template<typename T>
|
||||
static constexpr uint16_t _clampedUint16(T v) noexcept;
|
||||
template<typename T>
|
||||
constexpr uint16_t _clampedColumn(T v) const noexcept;
|
||||
template<typename T>
|
||||
constexpr uint16_t _clampedColumnInclusive(T v) const noexcept;
|
||||
|
||||
wchar_t _uncheckedChar(size_t off) const noexcept;
|
||||
uint16_t _charSize() const noexcept;
|
||||
uint16_t _uncheckedCharOffset(size_t col) const noexcept;
|
||||
bool _uncheckedIsTrailer(size_t col) const noexcept;
|
||||
|
||||
void _init() noexcept;
|
||||
void _resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd, size_t chExtEndNew);
|
||||
|
||||
// These fields are a bit "wasteful", but it makes all this a bit more robust against
|
||||
// programming errors during initial development (which is when this comment was written).
|
||||
// * _chars and _charsHeap are redundant
|
||||
// If _charsHeap is stored in _chars, we can still infer that
|
||||
// _chars was allocated on the heap if _chars != _charsBuffer.
|
||||
// * _chars doesn't need a size_t size()
|
||||
// The size may never exceed an uint16_t anyways.
|
||||
// * _charOffsets doesn't need a size() at all
|
||||
// The length is already stored in _columns.
|
||||
|
||||
// Most text uses only a single wchar_t per codepoint / grapheme cluster.
|
||||
// That's why TextBuffer allocates a large blob of virtual memory which we can use as
|
||||
// a simplified chars buffer, without having to allocate any additional heap memory.
|
||||
// _charsBuffer fits _columnCount characters at most.
|
||||
wchar_t* _charsBuffer = nullptr;
|
||||
// ...but if this ROW needs to store more than _columnCount characters
|
||||
// then it will allocate a larger string on the heap and store it here.
|
||||
// The capacity of this string on the heap is stored in _chars.size().
|
||||
std::unique_ptr<wchar_t[]> _charsHeap;
|
||||
// _chars either refers to our _charsBuffer or _charsHeap, defaulting to the former.
|
||||
// _chars.size() is NOT the length of the string, but rather its capacity.
|
||||
// _charOffsets[_columnCount] stores the length.
|
||||
std::span<wchar_t> _chars;
|
||||
// _charOffsets accelerates indexing into the above _chars string given a terminal column,
|
||||
// by storing the character index/offset at which a column's text in _chars starts.
|
||||
// It stores 1 more item than this row is wide, allowing it to store the
|
||||
// past-the-end offset, which is thus equal to the length of the string.
|
||||
//
|
||||
// For instance given a 4 column ROW containing "abcd" it would store 01234,
|
||||
// because each of "abcd" are 1 column wide and 1 wchar_t per column.
|
||||
// Given "a\u732Bd" it would store 01123, because "\u732B" is a wide glyph
|
||||
// and "11" indicates that both column 1 and 2 start at &_chars[1] (= wide glyph).
|
||||
// The fact that the next offset is 2 tells us that the glyph is 1 wchar_t long.
|
||||
// Given "a\uD83D\uDE00d" ("\uD83D\uDE00" is an Emoji) it would store 01134,
|
||||
// because while it's 2 cells wide as indicated by 2 offsets that are identical (11),
|
||||
// the next offset is 3, which indicates that the glyph is 3-1 = 2 wchar_t long.
|
||||
//
|
||||
// In other words, _charOffsets tells us both the width in chars and width in columns.
|
||||
// See CharOffsetsTrailer for more information.
|
||||
std::span<uint16_t> _charOffsets;
|
||||
// _attr is a run-length-encoded vector of TextAttribute with a decompressed
|
||||
// length equal to _columnCount (= 1 TextAttribute per column).
|
||||
til::small_rle<TextAttribute, uint16_t, 1> _attr;
|
||||
// The width of the row in visual columns.
|
||||
uint16_t _columnCount = 0;
|
||||
// Stores double-width/height (DECSWL/DECDWL/DECDHL) attributes.
|
||||
LineRendition _lineRendition = LineRendition::SingleWidth;
|
||||
// Occurs when the user runs out of text in a given row and we're forced to wrap the cursor to the next line
|
||||
bool _wrapForced;
|
||||
bool _wrapForced = false;
|
||||
// Occurs when the user runs out of text to support a double byte character and we're forced to the next line
|
||||
bool _doubleBytePadded;
|
||||
TextBuffer* _pParent; // non ownership pointer
|
||||
bool _doubleBytePadded = false;
|
||||
};
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
constexpr bool operator==(const ROW& a, const ROW& b) noexcept
|
||||
{
|
||||
// comparison is only used in the tests; this should suffice.
|
||||
return (a._pParent == b._pParent &&
|
||||
a._id == b._id);
|
||||
return a._charsBuffer == b._charsBuffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
// Keeping TextColor compact helps us keeping TextAttribute compact,
|
||||
// which in turn ensures that our buffer memory usage is low.
|
||||
static_assert(sizeof(TextAttribute) == 14);
|
||||
static_assert(sizeof(TextAttribute) == 12);
|
||||
static_assert(alignof(TextAttribute) == 2);
|
||||
// Ensure that we can memcpy() and memmove() the struct for performance.
|
||||
static_assert(std::is_trivially_copyable_v<TextAttribute>);
|
||||
@@ -116,7 +116,7 @@ WORD TextAttribute::GetLegacyAttributes() const noexcept
|
||||
{
|
||||
const auto fgIndex = _foreground.GetLegacyIndex(s_legacyDefaultForeground);
|
||||
const auto bgIndex = _background.GetLegacyIndex(s_legacyDefaultBackground);
|
||||
const WORD metaAttrs = _wAttrLegacy & META_ATTRS;
|
||||
const WORD metaAttrs = static_cast<WORD>(_attrs) & USED_META_ATTRS;
|
||||
const auto brighten = IsIntense() && _foreground.CanBeBrightened();
|
||||
return fgIndex | (bgIndex << 4) | metaAttrs | (brighten ? FOREGROUND_INTENSITY : 0);
|
||||
}
|
||||
@@ -217,151 +217,151 @@ void TextAttribute::SetHyperlinkId(uint16_t id) noexcept
|
||||
_hyperlinkId = id;
|
||||
}
|
||||
|
||||
bool TextAttribute::IsLeadingByte() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_LEADING_BYTE);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsTrailingByte() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_LEADING_BYTE);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsTopHorizontalDisplayed() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::TopGridline);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsBottomHorizontalDisplayed() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_UNDERSCORE);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::BottomGridline);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsLeftVerticalDisplayed() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_GRID_LVERTICAL);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::LeftGridline);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsRightVerticalDisplayed() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_GRID_RVERTICAL);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::RightGridline);
|
||||
}
|
||||
|
||||
void TextAttribute::SetLeftVerticalDisplayed(const bool isDisplayed) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_LVERTICAL, isDisplayed);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::LeftGridline, isDisplayed);
|
||||
}
|
||||
|
||||
void TextAttribute::SetRightVerticalDisplayed(const bool isDisplayed) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_RVERTICAL, isDisplayed);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::RightGridline, isDisplayed);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsIntense() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Intense);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::Intense);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsFaint() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Faint);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::Faint);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsItalic() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Italics);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::Italics);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsBlinking() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Blinking);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::Blinking);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsInvisible() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Invisible);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::Invisible);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsCrossedOut() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::CrossedOut);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::CrossedOut);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsUnderlined() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Underlined);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::Underlined);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsDoublyUnderlined() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::DoublyUnderlined);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::DoublyUnderlined);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsOverlined() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::TopGridline);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsReverseVideo() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO);
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::ReverseVideo);
|
||||
}
|
||||
|
||||
bool TextAttribute::IsProtected() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_attrs, CharacterAttributes::Protected);
|
||||
}
|
||||
|
||||
void TextAttribute::SetIntense(bool isIntense) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Intense, isIntense);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::Intense, isIntense);
|
||||
}
|
||||
|
||||
void TextAttribute::SetFaint(bool isFaint) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Faint, isFaint);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::Faint, isFaint);
|
||||
}
|
||||
|
||||
void TextAttribute::SetItalic(bool isItalic) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Italics, isItalic);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::Italics, isItalic);
|
||||
}
|
||||
|
||||
void TextAttribute::SetBlinking(bool isBlinking) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Blinking, isBlinking);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::Blinking, isBlinking);
|
||||
}
|
||||
|
||||
void TextAttribute::SetInvisible(bool isInvisible) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Invisible, isInvisible);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::Invisible, isInvisible);
|
||||
}
|
||||
|
||||
void TextAttribute::SetCrossedOut(bool isCrossedOut) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::CrossedOut, isCrossedOut);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::CrossedOut, isCrossedOut);
|
||||
}
|
||||
|
||||
void TextAttribute::SetUnderlined(bool isUnderlined) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Underlined, isUnderlined);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::Underlined, isUnderlined);
|
||||
}
|
||||
|
||||
void TextAttribute::SetDoublyUnderlined(bool isDoublyUnderlined) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::DoublyUnderlined, isDoublyUnderlined);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::DoublyUnderlined, isDoublyUnderlined);
|
||||
}
|
||||
|
||||
void TextAttribute::SetOverlined(bool isOverlined) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL, isOverlined);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::TopGridline, isOverlined);
|
||||
}
|
||||
|
||||
void TextAttribute::SetReverseVideo(bool isReversed) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO, isReversed);
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::ReverseVideo, isReversed);
|
||||
}
|
||||
|
||||
void TextAttribute::SetProtected(bool isProtected) noexcept
|
||||
{
|
||||
WI_UpdateFlag(_attrs, CharacterAttributes::Protected, isProtected);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - swaps foreground and background color
|
||||
void TextAttribute::Invert() noexcept
|
||||
{
|
||||
WI_ToggleFlag(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO);
|
||||
WI_ToggleFlag(_attrs, CharacterAttributes::ReverseVideo);
|
||||
}
|
||||
|
||||
void TextAttribute::SetDefaultForeground() noexcept
|
||||
@@ -375,11 +375,11 @@ void TextAttribute::SetDefaultBackground() noexcept
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Resets only the meta and extended attributes
|
||||
void TextAttribute::SetDefaultMetaAttrs() noexcept
|
||||
// - Resets only the rendition character attributes, which includes everything
|
||||
// except the Protected attribute.
|
||||
void TextAttribute::SetDefaultRenditionAttributes() noexcept
|
||||
{
|
||||
_extendedAttrs = ExtendedAttributes::Normal;
|
||||
_wAttrLegacy = 0;
|
||||
_attrs &= ~CharacterAttributes::Rendition;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -398,10 +398,11 @@ bool TextAttribute::BackgroundIsDefault() const noexcept
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Resets the meta and extended attributes, which is what the VT standard
|
||||
// requires for most erasing and filling operations.
|
||||
// - Resets the character attributes, which is what the VT standard
|
||||
// requires for most erasing and filling operations. In modern
|
||||
// applications it is also expected that hyperlinks are erased.
|
||||
void TextAttribute::SetStandardErase() noexcept
|
||||
{
|
||||
SetDefaultMetaAttrs();
|
||||
_attrs = CharacterAttributes::Normal;
|
||||
_hyperlinkId = 0;
|
||||
}
|
||||
|
||||
@@ -31,31 +31,26 @@ class TextAttribute final
|
||||
{
|
||||
public:
|
||||
constexpr TextAttribute() noexcept :
|
||||
_wAttrLegacy{ 0 },
|
||||
_attrs{ CharacterAttributes::Normal },
|
||||
_foreground{},
|
||||
_background{},
|
||||
_extendedAttrs{ ExtendedAttributes::Normal },
|
||||
_hyperlinkId{ 0 }
|
||||
{
|
||||
}
|
||||
|
||||
explicit constexpr TextAttribute(const WORD wLegacyAttr) noexcept :
|
||||
_wAttrLegacy{ gsl::narrow_cast<WORD>(wLegacyAttr & META_ATTRS) },
|
||||
_attrs{ gsl::narrow_cast<WORD>(wLegacyAttr & USED_META_ATTRS) },
|
||||
_foreground{ gsl::at(s_legacyForegroundColorMap, wLegacyAttr & FG_ATTRS) },
|
||||
_background{ gsl::at(s_legacyBackgroundColorMap, (wLegacyAttr & BG_ATTRS) >> 4) },
|
||||
_extendedAttrs{ ExtendedAttributes::Normal },
|
||||
_hyperlinkId{ 0 }
|
||||
{
|
||||
// If we're given lead/trailing byte information with the legacy color, strip it.
|
||||
WI_ClearAllFlags(_wAttrLegacy, COMMON_LVB_SBCSDBCS);
|
||||
}
|
||||
|
||||
constexpr TextAttribute(const COLORREF rgbForeground,
|
||||
const COLORREF rgbBackground) noexcept :
|
||||
_wAttrLegacy{ 0 },
|
||||
_attrs{ CharacterAttributes::Normal },
|
||||
_foreground{ rgbForeground },
|
||||
_background{ rgbBackground },
|
||||
_extendedAttrs{ ExtendedAttributes::Normal },
|
||||
_hyperlinkId{ 0 }
|
||||
{
|
||||
}
|
||||
@@ -64,8 +59,6 @@ public:
|
||||
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
|
||||
WORD GetLegacyAttributes() const noexcept;
|
||||
|
||||
bool IsLeadingByte() const noexcept;
|
||||
bool IsTrailingByte() const noexcept;
|
||||
bool IsTopHorizontalDisplayed() const noexcept;
|
||||
bool IsBottomHorizontalDisplayed() const noexcept;
|
||||
bool IsLeftVerticalDisplayed() const noexcept;
|
||||
@@ -97,6 +90,7 @@ public:
|
||||
bool IsDoublyUnderlined() const noexcept;
|
||||
bool IsOverlined() const noexcept;
|
||||
bool IsReverseVideo() const noexcept;
|
||||
bool IsProtected() const noexcept;
|
||||
|
||||
void SetIntense(bool isIntense) noexcept;
|
||||
void SetFaint(bool isFaint) noexcept;
|
||||
@@ -108,10 +102,15 @@ public:
|
||||
void SetDoublyUnderlined(bool isDoublyUnderlined) noexcept;
|
||||
void SetOverlined(bool isOverlined) noexcept;
|
||||
void SetReverseVideo(bool isReversed) noexcept;
|
||||
void SetProtected(bool isProtected) noexcept;
|
||||
|
||||
constexpr ExtendedAttributes GetExtendedAttributes() const noexcept
|
||||
constexpr void SetCharacterAttributes(const CharacterAttributes attrs) noexcept
|
||||
{
|
||||
return _extendedAttrs;
|
||||
_attrs = attrs;
|
||||
}
|
||||
constexpr CharacterAttributes GetCharacterAttributes() const noexcept
|
||||
{
|
||||
return _attrs;
|
||||
}
|
||||
|
||||
bool IsHyperlink() const noexcept;
|
||||
@@ -132,7 +131,7 @@ public:
|
||||
|
||||
void SetDefaultForeground() noexcept;
|
||||
void SetDefaultBackground() noexcept;
|
||||
void SetDefaultMetaAttrs() noexcept;
|
||||
void SetDefaultRenditionAttributes() noexcept;
|
||||
|
||||
bool BackgroundIsDefault() const noexcept;
|
||||
|
||||
@@ -149,38 +148,33 @@ public:
|
||||
const auto checkForeground = (inverted != IsReverseVideo());
|
||||
return !IsAnyGridLineEnabled() && // grid lines have a visual representation
|
||||
// crossed out, doubly and singly underlined have a visual representation
|
||||
WI_AreAllFlagsClear(_extendedAttrs, ExtendedAttributes::CrossedOut | ExtendedAttributes::DoublyUnderlined | ExtendedAttributes::Underlined) &&
|
||||
WI_AreAllFlagsClear(_attrs, CharacterAttributes::CrossedOut | CharacterAttributes::DoublyUnderlined | CharacterAttributes::Underlined) &&
|
||||
// hyperlinks have a visual representation
|
||||
!IsHyperlink() &&
|
||||
// all other attributes do not have a visual representation
|
||||
(_wAttrLegacy & META_ATTRS) == (other._wAttrLegacy & META_ATTRS) &&
|
||||
_attrs == other._attrs &&
|
||||
((checkForeground && _foreground == other._foreground) ||
|
||||
(!checkForeground && _background == other._background)) &&
|
||||
_extendedAttrs == other._extendedAttrs &&
|
||||
IsHyperlink() == other.IsHyperlink();
|
||||
}
|
||||
|
||||
constexpr bool IsAnyGridLineEnabled() const noexcept
|
||||
{
|
||||
return WI_IsAnyFlagSet(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_UNDERSCORE);
|
||||
return WI_IsAnyFlagSet(_attrs, CharacterAttributes::TopGridline | CharacterAttributes::LeftGridline | CharacterAttributes::RightGridline | CharacterAttributes::BottomGridline);
|
||||
}
|
||||
constexpr bool HasAnyExtendedAttributes() const noexcept
|
||||
constexpr bool HasAnyVisualAttributes() const noexcept
|
||||
{
|
||||
return GetExtendedAttributes() != ExtendedAttributes::Normal ||
|
||||
IsAnyGridLineEnabled() ||
|
||||
GetHyperlinkId() != 0 ||
|
||||
IsReverseVideo();
|
||||
return GetCharacterAttributes() != CharacterAttributes::Normal || GetHyperlinkId() != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::array<TextColor, 16> s_legacyForegroundColorMap;
|
||||
static std::array<TextColor, 16> s_legacyBackgroundColorMap;
|
||||
|
||||
uint16_t _wAttrLegacy; // sizeof: 2, alignof: 2
|
||||
CharacterAttributes _attrs; // sizeof: 2, alignof: 2
|
||||
uint16_t _hyperlinkId; // sizeof: 2, alignof: 2
|
||||
TextColor _foreground; // sizeof: 4, alignof: 1
|
||||
TextColor _background; // sizeof: 4, alignof: 1
|
||||
ExtendedAttributes _extendedAttrs; // sizeof: 2, alignof: 2
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
@@ -213,12 +207,11 @@ namespace WEX
|
||||
static WEX::Common::NoThrowString ToString(const TextAttribute& attr)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(
|
||||
L"{FG:%s,BG:%s,intense:%d,wLegacy:(0x%04x),ext:(0x%02x)}",
|
||||
L"{FG:%s,BG:%s,intense:%d,attrs:(0x%02x)}",
|
||||
VerifyOutputTraits<TextColor>::ToString(attr._foreground).GetBuffer(),
|
||||
VerifyOutputTraits<TextColor>::ToString(attr._background).GetBuffer(),
|
||||
attr.IsIntense(),
|
||||
attr._wAttrLegacy,
|
||||
static_cast<DWORD>(attr._extendedAttrs));
|
||||
static_cast<DWORD>(attr._attrs));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "UnicodeStorage.hpp"
|
||||
|
||||
UnicodeStorage::UnicodeStorage() noexcept :
|
||||
_map{}
|
||||
{
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - fetches the text associated with key
|
||||
// Arguments:
|
||||
// - key - the key into the storage
|
||||
// Return Value:
|
||||
// - the glyph data associated with key
|
||||
// Note: will throw exception if key is not stored yet
|
||||
const UnicodeStorage::mapped_type& UnicodeStorage::GetText(const key_type key) const
|
||||
{
|
||||
return _map.at(key);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - stores glyph data associated with key.
|
||||
// Arguments:
|
||||
// - key - the key into the storage
|
||||
// - glyph - the glyph data to store
|
||||
void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph)
|
||||
{
|
||||
_map.insert_or_assign(key, glyph);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - erases key and its associated data from the storage
|
||||
// Arguments:
|
||||
// - key - the key to remove
|
||||
void UnicodeStorage::Erase(const key_type key) noexcept
|
||||
{
|
||||
_map.erase(key);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Remaps all of the stored items to new coordinate positions
|
||||
// based on a bulk rearrangement of row IDs and potential row width resize.
|
||||
// Arguments:
|
||||
// - rowMap - A map of the old row IDs to the new row IDs.
|
||||
// - width - The width of the new row. Remove any items that are beyond the row width.
|
||||
// - Use nullopt if we're not resizing the width of the row, just renumbering the rows.
|
||||
void UnicodeStorage::Remap(const std::unordered_map<til::CoordType, til::CoordType>& rowMap, const std::optional<til::CoordType> width)
|
||||
{
|
||||
// Make a temporary map to hold all the new row positioning
|
||||
std::unordered_map<key_type, mapped_type> newMap;
|
||||
|
||||
// Walk through every stored item.
|
||||
for (const auto& pair : _map)
|
||||
{
|
||||
// Extract the old coordinate position
|
||||
const auto oldCoord = pair.first;
|
||||
|
||||
// Only try to short-circuit based on width if we were told it changed
|
||||
// by being given a new width value.
|
||||
if (width.has_value())
|
||||
{
|
||||
// Get the column ID
|
||||
const auto oldColId = oldCoord.X;
|
||||
|
||||
// If the column index is at/beyond the row width, don't bother copying it to the new map.
|
||||
if (oldColId >= width.value())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the row ID from the position as that's what we need to remap
|
||||
const auto oldRowId = oldCoord.Y;
|
||||
|
||||
// Use the mapping given to convert the old row ID to the new row ID
|
||||
const auto mapIter = rowMap.find(oldRowId);
|
||||
|
||||
// If there's no mapping to a new row, don't bother copying it to the new map. The row is gone.
|
||||
if (mapIter == rowMap.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto newRowId = mapIter->second;
|
||||
|
||||
// Generate a new coordinate with the same X as the old one, but a new Y value.
|
||||
const auto newCoord = til::point{ oldCoord.X, newRowId };
|
||||
|
||||
// Put the adjusted coordinate into the map with the original value.
|
||||
newMap.emplace(newCoord, pair.second);
|
||||
}
|
||||
|
||||
// Swap into the stored map, free the temporary when we exit.
|
||||
_map.swap(newMap);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user