mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-07 14:50:55 +00:00
Compare commits
268 Commits
dev/miniks
...
v1.1.1671.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1076a823d | ||
|
|
888e72417d | ||
|
|
332d1744d9 | ||
|
|
6d2fc5d1b9 | ||
|
|
5e2c4c66e3 | ||
|
|
c0ffc9b6dc | ||
|
|
c360b7588f | ||
|
|
19fcbce459 | ||
|
|
25df527743 | ||
|
|
e8ece1645c | ||
|
|
827cc42a45 | ||
|
|
db518c0b06 | ||
|
|
bcdccc533e | ||
|
|
60630cf7c4 | ||
|
|
7fc735525d | ||
|
|
30a6c1e10a | ||
|
|
a921bbfebb | ||
|
|
0c93b2ebbe | ||
|
|
8dcfd61278 | ||
|
|
9ce884c4fb | ||
|
|
7b791f572d | ||
|
|
e03e46b69e | ||
|
|
f9b1238f30 | ||
|
|
48b99faed1 | ||
|
|
8b201c1eb0 | ||
|
|
968462fffd | ||
|
|
f32761849f | ||
|
|
55d8df4628 | ||
|
|
e3ee5838a7 | ||
|
|
ccea66710c | ||
|
|
4a1f2d3c09 | ||
|
|
2aaad9688a | ||
|
|
2d18874c8e | ||
|
|
935702cde9 | ||
|
|
f90f3bf998 | ||
|
|
b47df4b19d | ||
|
|
b822cfb178 | ||
|
|
026ba34de1 | ||
|
|
bb4c73cc79 | ||
|
|
c3ea7310c1 | ||
|
|
038d6e5faf | ||
|
|
b051e78d40 | ||
|
|
1711c76234 | ||
|
|
e455d4b159 | ||
|
|
f1c9fbd080 | ||
|
|
efce279c5c | ||
|
|
96a77cb74b | ||
|
|
b1715479bd | ||
|
|
7b489128ac | ||
|
|
1fcd95704d | ||
|
|
e1888b0a29 | ||
|
|
52c0145c64 | ||
|
|
fe86844996 | ||
|
|
18e75bb954 | ||
|
|
9b075e985a | ||
|
|
413b658805 | ||
|
|
eccfb537ad | ||
|
|
f5dad2f586 | ||
|
|
88db0e6102 | ||
|
|
5e51ba4d1f | ||
|
|
a34f1e2ad4 | ||
|
|
7a258f934b | ||
|
|
48b3262eaa | ||
|
|
8987486e85 | ||
|
|
d92c8293ce | ||
|
|
44dcc861ad | ||
|
|
9ed776bf3e | ||
|
|
fc8fff17db | ||
|
|
94eab6e391 | ||
|
|
601286ac69 | ||
|
|
af56088cb6 | ||
|
|
ba03068940 | ||
|
|
d91e755fe7 | ||
|
|
e390111dfb | ||
|
|
c30aff0c49 | ||
|
|
f17b6c7d0d | ||
|
|
807d2cf2d2 | ||
|
|
1fc0997969 | ||
|
|
cc472c267b | ||
|
|
fa7c1abdf8 | ||
|
|
e7c22fbadb | ||
|
|
1ea08892f8 | ||
|
|
f1b67b3683 | ||
|
|
0582a6576a | ||
|
|
8752054f5b | ||
|
|
2af722b43d | ||
|
|
c373ebcd8d | ||
|
|
a472d21d89 | ||
|
|
8265d941b7 | ||
|
|
99a6fa4421 | ||
|
|
5d6fdf3897 | ||
|
|
c78f264b4b | ||
|
|
c1df53f014 | ||
|
|
cacfbb5db2 | ||
|
|
ef45b0c358 | ||
|
|
ba1a298d6b | ||
|
|
e1c139128e | ||
|
|
b46d393061 | ||
|
|
6394e5d70b | ||
|
|
71e29b1617 | ||
|
|
122e4b8124 | ||
|
|
c39f9c6626 | ||
|
|
99037c968d | ||
|
|
2453e8e1ed | ||
|
|
1422714af6 | ||
|
|
b4c33dd842 | ||
|
|
88ed94d2ac | ||
|
|
cf62922ad8 | ||
|
|
f9ec9b7f2e | ||
|
|
c13d6f804a | ||
|
|
a99c812794 | ||
|
|
fcd5bb39f0 | ||
|
|
e088f73671 | ||
|
|
d01317c9db | ||
|
|
177fd74584 | ||
|
|
e7a2732ffb | ||
|
|
f701bd40c5 | ||
|
|
7ae34336da | ||
|
|
75752aed80 | ||
|
|
38472719d5 | ||
|
|
e3fdb1a1a3 | ||
|
|
70867df077 | ||
|
|
d77745d035 | ||
|
|
28dc8196ab | ||
|
|
9a927f3a0f | ||
|
|
1d72e771b5 | ||
|
|
25f650578e | ||
|
|
92812bf316 | ||
|
|
ce91c164c2 | ||
|
|
8e67bb2d4f | ||
|
|
9e1d2d2e63 | ||
|
|
0eaa7214ae | ||
|
|
7a251d85f7 | ||
|
|
fb08b6bf49 | ||
|
|
046fed3d97 | ||
|
|
7d54bc5ecb | ||
|
|
9fe624ffbc | ||
|
|
9166a14f4b | ||
|
|
624a553d23 | ||
|
|
6ce3357bab | ||
|
|
48617b7e06 | ||
|
|
9c52920612 | ||
|
|
8a15b2f69b | ||
|
|
6cbc2e5fa4 | ||
|
|
44689b93a2 | ||
|
|
b6e46c0dc1 | ||
|
|
d57ef135cc | ||
|
|
7612044363 | ||
|
|
b6a21de802 | ||
|
|
4286877864 | ||
|
|
3246c4250a | ||
|
|
17bd8b5750 | ||
|
|
fb22eae072 | ||
|
|
10fa3108e1 | ||
|
|
ea02128f11 | ||
|
|
e3a99f6879 | ||
|
|
e8a68114e8 | ||
|
|
da7610117a | ||
|
|
1ce86f8f1a | ||
|
|
6d4107206a | ||
|
|
db5d2fe0a5 | ||
|
|
9247ff081b | ||
|
|
e358b96746 | ||
|
|
9e23ee7fd6 | ||
|
|
64f8bbf5ff | ||
|
|
97d6456476 | ||
|
|
d6cae40d26 | ||
|
|
b4c9c0cfeb | ||
|
|
8004223500 | ||
|
|
37ff3f9f87 | ||
|
|
7eb0a26b91 | ||
|
|
214163ebb7 | ||
|
|
79959dbbbf | ||
|
|
98ac196cec | ||
|
|
757db46aa6 | ||
|
|
c803893c22 | ||
|
|
fa58d87761 | ||
|
|
22d2096bdc | ||
|
|
a76cee1d98 | ||
|
|
7bf6163e63 | ||
|
|
6ac6c87553 | ||
|
|
8ea9b327f3 | ||
|
|
0741cfdaec | ||
|
|
632e8ff8ac | ||
|
|
fde662f4e2 | ||
|
|
f7e2159310 | ||
|
|
4d8c26aaae | ||
|
|
57fd7baff4 | ||
|
|
ad9627db89 | ||
|
|
f6f0731eae | ||
|
|
c6cb3b53dd | ||
|
|
86685079ec | ||
|
|
bc6ea11233 | ||
|
|
6995d0b2dc | ||
|
|
13e31a0056 | ||
|
|
252c7d36a4 | ||
|
|
8a976c0d23 | ||
|
|
e3e57081a2 | ||
|
|
e271599bf4 | ||
|
|
152b399e90 | ||
|
|
56259ea005 | ||
|
|
5740e197c2 | ||
|
|
38e7cb0742 | ||
|
|
bceeb4523e | ||
|
|
f1fe4c6ccd | ||
|
|
f04b7aae87 | ||
|
|
a6b2e7f612 | ||
|
|
7f886620b3 | ||
|
|
c186c7d683 | ||
|
|
fe3f528827 | ||
|
|
dc43524eb2 | ||
|
|
37e62dabd5 | ||
|
|
348f46866a | ||
|
|
9dd5f6e8b9 | ||
|
|
d46eead61b | ||
|
|
e61968ca87 | ||
|
|
22e2774c21 | ||
|
|
114e462669 | ||
|
|
05119e2faf | ||
|
|
ea61aa3bfc | ||
|
|
3927c68a00 | ||
|
|
79684bf821 | ||
|
|
ffbdfe32ac | ||
|
|
ea1bb2ed93 | ||
|
|
08df4fc27a | ||
|
|
6f0f245dd0 | ||
|
|
c65de31172 | ||
|
|
2bd1e398bd | ||
|
|
a09989749a | ||
|
|
2d09dfd48b | ||
|
|
7936605cc9 | ||
|
|
edf79b281a | ||
|
|
c55d9cd62d | ||
|
|
191b0d288a | ||
|
|
1299a839bd | ||
|
|
6fabc4abb7 | ||
|
|
5622f230a8 | ||
|
|
761bee80c0 | ||
|
|
26778440b3 | ||
|
|
7335232cda | ||
|
|
7c7db79782 | ||
|
|
a9c9714295 | ||
|
|
52d6c03e64 | ||
|
|
e44dc2bcf8 | ||
|
|
38803d7d05 | ||
|
|
6d4c44f3a4 | ||
|
|
b1a5604b55 | ||
|
|
4f8acb4b9f | ||
|
|
94215535cd | ||
|
|
286af380c9 | ||
|
|
9513d543b7 | ||
|
|
ecc6c89dde | ||
|
|
f57ed0518a | ||
|
|
084b48a751 | ||
|
|
90a78d65ac | ||
|
|
bdbf40451e | ||
|
|
584e4498ee | ||
|
|
713550d56c | ||
|
|
9409e851d0 | ||
|
|
64489b1ec1 | ||
|
|
c1463bdbf0 | ||
|
|
a12a6285f5 | ||
|
|
f8227e6fa2 | ||
|
|
8585bc6bde | ||
|
|
9a0b6e3b69 | ||
|
|
a621a6fabb | ||
|
|
cd9e854553 | ||
|
|
ca6b54e652 |
25
.github/actions/spell-check/advice.txt
vendored
Normal file
25
.github/actions/spell-check/advice.txt
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<details>
|
||||
<summary>
|
||||
:pencil2: Contributor please read this
|
||||
</summary>
|
||||
|
||||
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
|
||||
|
||||
:warning: The command is written for posix shells. You can copy the contents of each `perl` command excluding the outer `'` marks and dropping any `'"`/`"'` quotation mark pairs into a file and then run `perl file.pl` from the root of the repository to run the code. Alternatively, you can manually insert the items...
|
||||
|
||||
If the listed items are:
|
||||
* ... **misspelled**, then please *correct* them instead of using the command.
|
||||
* ... *names*, please add them to `.github/actions/spell-check/dictionary/names.txt`.
|
||||
* ... APIs, you can add them to a file in `.github/actions/spell-check/dictionary/`.
|
||||
* ... just things you're using, please add them to an appropriate file in `.github/actions/spell-check/expect/`.
|
||||
* ... tokens you only need in one place and shouldn't *generally be used*, you can add an item in an appropriate file in `.github/actions/spell-check/patterns/`.
|
||||
|
||||
See the `README.md` in each directory for more information.
|
||||
|
||||
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [:check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
|
||||
|
||||
</details>
|
||||
|
||||
#### :warning: Reviewers
|
||||
At present, the action that triggered this message will not show its :x: in this PR unless the branch is within this repository.
|
||||
Thus, you **should** make sure that this comment has been addressed before encouraging the merge bot to merge this PR.
|
||||
0
.github/actions/spell-check/dictionary/api.txt
vendored
Normal file
0
.github/actions/spell-check/dictionary/api.txt
vendored
Normal file
35
.github/actions/spell-check/dictionary/apis.txt
vendored
Normal file
35
.github/actions/spell-check/dictionary/apis.txt
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
ACCEPTFILES
|
||||
ACCESSDENIED
|
||||
bitfield
|
||||
bitfields
|
||||
CLASSNOTAVAILABLE
|
||||
EXPCMDFLAGS
|
||||
EXPCMDSTATE
|
||||
fullkbd
|
||||
href
|
||||
IAsync
|
||||
IBox
|
||||
IBind
|
||||
IClass
|
||||
IComparable
|
||||
ICustom
|
||||
IDirect
|
||||
IExplorer
|
||||
IMap
|
||||
IObject
|
||||
IStorage
|
||||
LCID
|
||||
LSHIFT
|
||||
NCHITTEST
|
||||
NCLBUTTONDBLCLK
|
||||
NCRBUTTONDBLCLK
|
||||
NOAGGREGATION
|
||||
NOREDIRECTIONBITMAP
|
||||
oaidl
|
||||
ocidl
|
||||
RETURNCMD
|
||||
rfind
|
||||
roundf
|
||||
RSHIFT
|
||||
SIZENS
|
||||
tmp
|
||||
@@ -28673,11 +28673,8 @@ attacco
|
||||
attach
|
||||
attachable
|
||||
attachableness
|
||||
attache
|
||||
attached
|
||||
attachedly
|
||||
attacher
|
||||
attachers
|
||||
attaches
|
||||
attacheship
|
||||
attaching
|
||||
@@ -76036,6 +76033,7 @@ Clichy
|
||||
Clichy-la-Garenne
|
||||
click
|
||||
click-clack
|
||||
clickable
|
||||
clicked
|
||||
clicker
|
||||
clickers
|
||||
@@ -97786,6 +97784,7 @@ dalesman
|
||||
dalesmen
|
||||
dalespeople
|
||||
daleswoman
|
||||
dalet
|
||||
daleth
|
||||
daleths
|
||||
Daleville
|
||||
@@ -107561,6 +107560,7 @@ dialystelic
|
||||
dialystely
|
||||
dialytic
|
||||
dialytically
|
||||
dialytika
|
||||
dialyzability
|
||||
dialyzable
|
||||
dialyzate
|
||||
@@ -114004,6 +114004,7 @@ djalmaite
|
||||
Djambi
|
||||
djasakid
|
||||
djave
|
||||
dje
|
||||
djebel
|
||||
djebels
|
||||
djehad
|
||||
@@ -120418,11 +120419,13 @@ DZ
|
||||
dz
|
||||
dz.
|
||||
Dzaudzhikau
|
||||
dze
|
||||
dzeren
|
||||
dzerin
|
||||
dzeron
|
||||
Dzerzhinsk
|
||||
Dzhambul
|
||||
dzhe
|
||||
Dzhugashvili
|
||||
dziggetai
|
||||
dzo
|
||||
@@ -158966,6 +158969,7 @@ Ghaznevid
|
||||
Ghazzah
|
||||
Ghazzali
|
||||
ghbor
|
||||
ghe
|
||||
Gheber
|
||||
gheber
|
||||
ghebeta
|
||||
@@ -160166,6 +160170,7 @@ gizzards
|
||||
gizzen
|
||||
gizzened
|
||||
gizzern
|
||||
gje
|
||||
gjedost
|
||||
Gjellerup
|
||||
gjetost
|
||||
@@ -186980,6 +186985,8 @@ hyperleucocytosis
|
||||
hyperleucocytotic
|
||||
hyperleukocytosis
|
||||
hyperlexis
|
||||
hyperlink
|
||||
hyperlinking
|
||||
hyperlipaemia
|
||||
hyperlipaemic
|
||||
hyperlipemia
|
||||
@@ -212347,6 +212354,7 @@ Kizilbash
|
||||
Kizzee
|
||||
Kizzie
|
||||
kJ
|
||||
kje
|
||||
Kjeldahl
|
||||
kjeldahlization
|
||||
kjeldahlize
|
||||
@@ -224856,6 +224864,7 @@ lizzie
|
||||
Lizzy
|
||||
LJ
|
||||
LJBF
|
||||
lje
|
||||
Ljod
|
||||
Ljoka
|
||||
Ljubljana
|
||||
@@ -261607,6 +261616,7 @@ N.J.
|
||||
NJ
|
||||
nj
|
||||
njave
|
||||
nje
|
||||
Njord
|
||||
Njorth
|
||||
NKGB
|
||||
@@ -274785,6 +274795,7 @@ Ogma
|
||||
ogmic
|
||||
Ogmios
|
||||
OGO
|
||||
ogonek
|
||||
ogonium
|
||||
Ogor
|
||||
O'Gowan
|
||||
@@ -329834,6 +329845,7 @@ QN
|
||||
qn
|
||||
QNP
|
||||
QNS
|
||||
qof
|
||||
Qoheleth
|
||||
Qom
|
||||
qoph
|
||||
@@ -371408,6 +371420,7 @@ Shaysite
|
||||
shazam
|
||||
Shazar
|
||||
SHCD
|
||||
shcha
|
||||
Shcheglovsk
|
||||
Shcherbakov
|
||||
she
|
||||
@@ -384723,23 +384736,7 @@ spadonic
|
||||
spadonism
|
||||
spadrone
|
||||
spadroon
|
||||
spae
|
||||
spaebook
|
||||
spaecraft
|
||||
spaed
|
||||
spaedom
|
||||
spaeing
|
||||
spaeings
|
||||
spae-man
|
||||
spaeman
|
||||
spaer
|
||||
Spaerobee
|
||||
spaes
|
||||
spaetzle
|
||||
spaewife
|
||||
spaewoman
|
||||
spaework
|
||||
spaewright
|
||||
SPAG
|
||||
spag
|
||||
spagetti
|
||||
@@ -420989,6 +420986,7 @@ tonometry
|
||||
Tonopah
|
||||
tonophant
|
||||
tonoplast
|
||||
tonos
|
||||
tonoscope
|
||||
tonotactic
|
||||
tonotaxis
|
||||
@@ -428692,6 +428690,7 @@ tsetses
|
||||
TSF
|
||||
TSgt
|
||||
TSH
|
||||
tshe
|
||||
Tshi
|
||||
tshi
|
||||
Tshiluba
|
||||
@@ -468746,11 +468745,8 @@ wetchet
|
||||
wet-clean
|
||||
wet-eyed
|
||||
wet-footed
|
||||
wether
|
||||
wetherhog
|
||||
wethers
|
||||
Wethersfield
|
||||
wetherteg
|
||||
wetland
|
||||
wetlands
|
||||
wet-lipped
|
||||
@@ -470904,6 +470900,7 @@ wild-born
|
||||
wild-brained
|
||||
wild-bred
|
||||
wildcard
|
||||
wildcards
|
||||
wildcat
|
||||
wildcats
|
||||
wildcatted
|
||||
@@ -477087,6 +477084,7 @@ Yermo
|
||||
yern
|
||||
yertchuk
|
||||
yerth
|
||||
yeru
|
||||
yerva
|
||||
Yerwa-Maiduguri
|
||||
Yerxa
|
||||
@@ -478254,6 +478252,7 @@ Z-bar
|
||||
ZBB
|
||||
ZBR
|
||||
ZD
|
||||
ze
|
||||
Zea
|
||||
zea
|
||||
zeal
|
||||
@@ -478623,6 +478622,7 @@ ZG
|
||||
ZGS
|
||||
Zhang
|
||||
Zhdanov
|
||||
zhe
|
||||
Zhitomir
|
||||
Zhivkov
|
||||
Zhmud
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
ACLs
|
||||
DACL
|
||||
DACLs
|
||||
LKG
|
||||
mfcribbon
|
||||
microsoft
|
||||
microsoftonline
|
||||
osgvsowi
|
||||
powerrename
|
||||
powershell
|
||||
SACLs
|
||||
tdbuildteamid
|
||||
vcruntime
|
||||
visualstudio
|
||||
|
||||
@@ -3,8 +3,10 @@ austdi
|
||||
Ballmer
|
||||
bhoj
|
||||
Bhojwani
|
||||
carlos
|
||||
dhowett
|
||||
Diviness
|
||||
dsafa
|
||||
duhowett
|
||||
ethanschoonover
|
||||
Firefox
|
||||
@@ -38,6 +40,7 @@ oldnewthing
|
||||
osgwiki
|
||||
paulcam
|
||||
pauldotknopf
|
||||
PGP
|
||||
Pham
|
||||
Rincewind
|
||||
rprichard
|
||||
@@ -51,5 +54,8 @@ Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
Zamor
|
||||
Zamora
|
||||
zamora
|
||||
Zoey
|
||||
zorio
|
||||
Zverovich
|
||||
|
||||
2
.github/actions/spell-check/excludes.txt
vendored
2
.github/actions/spell-check/excludes.txt
vendored
@@ -51,7 +51,9 @@ SUMS$
|
||||
\.xpm$
|
||||
\.yml$
|
||||
\.zip$
|
||||
^consolegit2gitfilters\.json$
|
||||
^dep/
|
||||
^oss/
|
||||
^doc/reference/UTF8-torture-test\.txt$
|
||||
^src/interactivity/onecore/BgfxEngine\.
|
||||
^src/renderer/wddmcon/WddmConRenderer\.
|
||||
|
||||
13
.github/actions/spell-check/expect/README.md
vendored
Normal file
13
.github/actions/spell-check/expect/README.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
The contents of each `.txt` file in this directory are merged together.
|
||||
|
||||
* [alphabet](alphabet.txt) is a sample for alphabet related items
|
||||
* [web](web.txt) is a sample for web/html related items
|
||||
* [expect](expect.txt) is the main list of expected items -- there is nothing
|
||||
particularly special about the file name (beyond the extension which is
|
||||
important).
|
||||
|
||||
These terms are things which temporarily exist in the project, but which
|
||||
aren't necessarily words.
|
||||
|
||||
If something is a word that could come and go, it probably belongs in a
|
||||
[dictionary](../dictionary/README.md).
|
||||
@@ -1,4 +1,3 @@
|
||||
abcd
|
||||
abcde
|
||||
abcdef
|
||||
ABCDEFG
|
||||
@@ -8,9 +7,8 @@ abcdefghijk
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
QQQQQ
|
||||
QQQQQQQQQ
|
||||
QQQQQQQQQQ
|
||||
ABE
|
||||
BBBBBBBBBBBBBBDDDD
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
@@ -19,9 +17,7 @@ qrstuvwxyz
|
||||
qwerty
|
||||
QWERTYUIOP
|
||||
qwertyuiopasdfg
|
||||
TTTTTTTTTTTTTTTTTTTTTTTTTT
|
||||
VVVVVVVVVVVVVVVV
|
||||
yyyy
|
||||
YYYYYYYDDDDDDDDDDD
|
||||
ZAAZZ
|
||||
ZABBZ
|
||||
ZBAZZ
|
||||
@@ -31,4 +27,3 @@ ZYXWVUT
|
||||
ZZBBZ
|
||||
ZZZBB
|
||||
ZZZBZ
|
||||
ZZZZZ
|
||||
@@ -1,20 +1,12 @@
|
||||
aaaaa
|
||||
aaaaaa
|
||||
aaaaaaaaaaaaaaaaaaa
|
||||
AAAAAABBBBBBCCC
|
||||
AAAAABBBBBBBCCC
|
||||
AAD
|
||||
AAAAABCCCCCCCCC
|
||||
AAAAADCCCCCCCCC
|
||||
ABANDONFONT
|
||||
ABCDEFGHIJKLMNO
|
||||
ABCG
|
||||
abe
|
||||
abec
|
||||
abf
|
||||
abi
|
||||
acb
|
||||
accd
|
||||
ACCEPTFILES
|
||||
ACCESSDENIED
|
||||
acec
|
||||
acf
|
||||
acidev
|
||||
@@ -30,10 +22,7 @@ ADDREF
|
||||
addressof
|
||||
ADDSTRING
|
||||
ADDTOOL
|
||||
aeed
|
||||
AEnd
|
||||
afae
|
||||
afceeeaa
|
||||
AFew
|
||||
AFill
|
||||
AFX
|
||||
@@ -77,7 +66,6 @@ applet
|
||||
appletname
|
||||
applicationmodel
|
||||
APPLMODAL
|
||||
APPLOGIC
|
||||
appmodel
|
||||
apps
|
||||
APPWINDOW
|
||||
@@ -133,36 +121,25 @@ awch
|
||||
azuredevopspodcast
|
||||
azurewebsites
|
||||
azzle
|
||||
baac
|
||||
backend
|
||||
backgrounded
|
||||
backgrounder
|
||||
Backgrounder
|
||||
backgrounding
|
||||
backport
|
||||
backstory
|
||||
Batang
|
||||
baz
|
||||
Bazz
|
||||
BBBB
|
||||
BBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||
BBBBCCCCC
|
||||
bbc
|
||||
bbd
|
||||
BBDM
|
||||
bbe
|
||||
bbwe
|
||||
bc
|
||||
bca
|
||||
bcb
|
||||
bcc
|
||||
bcount
|
||||
bcrypt
|
||||
bcx
|
||||
bcz
|
||||
bda
|
||||
BEFOREPARENT
|
||||
beginthread
|
||||
bfb
|
||||
bgcolor
|
||||
bgfx
|
||||
bgidx
|
||||
@@ -176,7 +153,6 @@ binplace
|
||||
binplaced
|
||||
bitcoin
|
||||
bitcrazed
|
||||
bitfield
|
||||
bitflag
|
||||
bitmask
|
||||
BITOPERATION
|
||||
@@ -219,7 +195,6 @@ BValue
|
||||
byref
|
||||
bytearray
|
||||
bytebuffer
|
||||
caa
|
||||
Cac
|
||||
callee
|
||||
cang
|
||||
@@ -230,7 +205,6 @@ cascadia
|
||||
catid
|
||||
cazamor
|
||||
CBash
|
||||
cbb
|
||||
cbegin
|
||||
cbiex
|
||||
CBN
|
||||
@@ -238,7 +212,6 @@ CBoolean
|
||||
cbt
|
||||
cbuffer
|
||||
CCCBB
|
||||
CCCC
|
||||
ccf
|
||||
cch
|
||||
CCHAR
|
||||
@@ -252,12 +225,9 @@ CConversion
|
||||
CCRT
|
||||
cctype
|
||||
CDATA
|
||||
CDBA
|
||||
cdd
|
||||
CDE
|
||||
cdecl
|
||||
CDeclaration
|
||||
cebb
|
||||
CEdit
|
||||
CELLSIZE
|
||||
cend
|
||||
@@ -318,12 +288,11 @@ cnt
|
||||
CNTRL
|
||||
codebase
|
||||
Codeflow
|
||||
codeofconduct
|
||||
codepage
|
||||
codepoint
|
||||
codeproject
|
||||
COINIT
|
||||
colo
|
||||
COLOREREF
|
||||
colorizing
|
||||
colororacle
|
||||
colorref
|
||||
@@ -400,7 +369,6 @@ constness
|
||||
contentfiles
|
||||
conterm
|
||||
CONTEXTMENU
|
||||
controlkeystates
|
||||
contsf
|
||||
contypes
|
||||
convarea
|
||||
@@ -472,9 +440,7 @@ CURRENTPAGE
|
||||
CURSORCOLOR
|
||||
CURSORSIZE
|
||||
CURSORTYPE
|
||||
customframe
|
||||
CUU
|
||||
cvd
|
||||
cw
|
||||
Cwa
|
||||
cwch
|
||||
@@ -507,18 +473,12 @@ CYVSCROLL
|
||||
dahall
|
||||
dai
|
||||
DATABLOCK
|
||||
dataprotection
|
||||
dataprotectionprovider
|
||||
DATAVIEW
|
||||
DATAW
|
||||
dba
|
||||
DBatch
|
||||
dbb
|
||||
dbcs
|
||||
DBCSCHAR
|
||||
DBCSFONT
|
||||
dbd
|
||||
DBDCF
|
||||
dbg
|
||||
DBGALL
|
||||
DBGCHARS
|
||||
@@ -529,14 +489,11 @@ dblclk
|
||||
DBlob
|
||||
dbproj
|
||||
DBUILD
|
||||
dcf
|
||||
DColor
|
||||
DCOLORVALUE
|
||||
dcommon
|
||||
DCompile
|
||||
dcompiler
|
||||
ddb
|
||||
DDDD
|
||||
dde
|
||||
DDESHARE
|
||||
DDevice
|
||||
@@ -551,6 +508,7 @@ DECAUPSS
|
||||
DECAWM
|
||||
DECCKM
|
||||
DECCOLM
|
||||
DECEKBD
|
||||
decf
|
||||
DECKPAM
|
||||
DECKPM
|
||||
@@ -568,6 +526,7 @@ deconstructed
|
||||
DECPCTERM
|
||||
DECRC
|
||||
DECRLM
|
||||
DECRQM
|
||||
DECRST
|
||||
DECSASD
|
||||
DECSC
|
||||
@@ -580,6 +539,7 @@ DECSEL
|
||||
DECSET
|
||||
DECSLRM
|
||||
DECSMBV
|
||||
DECSMKR
|
||||
DECSR
|
||||
decstandar
|
||||
DECSTBM
|
||||
@@ -608,6 +568,7 @@ Delt
|
||||
demoable
|
||||
depersist
|
||||
deprioritized
|
||||
deps
|
||||
deque
|
||||
deref
|
||||
deserialization
|
||||
@@ -625,14 +586,13 @@ devicefamily
|
||||
devops
|
||||
Dext
|
||||
df
|
||||
dfa
|
||||
DFactory
|
||||
dh
|
||||
dialogbox
|
||||
diffing
|
||||
DINLINE
|
||||
directio
|
||||
directx
|
||||
DIRECTX
|
||||
Dirs
|
||||
DISABLENOSCROLL
|
||||
DISPLAYATTRIBUTE
|
||||
@@ -646,6 +606,7 @@ dllexport
|
||||
DLLGETVERSIONPROC
|
||||
dllimport
|
||||
dllinit
|
||||
dllmain
|
||||
DLLVERSIONINFO
|
||||
DLOAD
|
||||
DLOOK
|
||||
@@ -663,7 +624,6 @@ DPICHANGE
|
||||
DPICHANGED
|
||||
dpix
|
||||
dpiy
|
||||
draggable
|
||||
DRAWFRAME
|
||||
DRAWITEM
|
||||
DRAWITEMSTRUCT
|
||||
@@ -694,14 +654,9 @@ dxgidwm
|
||||
dxinterop
|
||||
dxttbmp
|
||||
eachother
|
||||
EAEC
|
||||
eaf
|
||||
EASTEUROPE
|
||||
eb
|
||||
eba
|
||||
ebce
|
||||
EBFB
|
||||
ECFB
|
||||
ECH
|
||||
echokey
|
||||
ecount
|
||||
@@ -711,9 +666,6 @@ EDITTEXT
|
||||
EDITUPDATE
|
||||
edputil
|
||||
edu
|
||||
eeb
|
||||
eee
|
||||
EEEE
|
||||
Efast
|
||||
EHsc
|
||||
EJO
|
||||
@@ -723,7 +675,6 @@ Elems
|
||||
elif
|
||||
elseif
|
||||
emacs
|
||||
Emojis
|
||||
emptybox
|
||||
enabledelayedexpansion
|
||||
endian
|
||||
@@ -742,6 +693,7 @@ ENUMLOGFONTEX
|
||||
enumranges
|
||||
envvar
|
||||
eol
|
||||
EPres
|
||||
ERASEBKGND
|
||||
errno
|
||||
errorlevel
|
||||
@@ -778,33 +730,21 @@ FACENODE
|
||||
FACESIZE
|
||||
failfast
|
||||
FAILIFTHERE
|
||||
FAILOUT
|
||||
fallthrough
|
||||
FARPROC
|
||||
fba
|
||||
fbb
|
||||
fbd
|
||||
fbdc
|
||||
FBE
|
||||
fcb
|
||||
fcc
|
||||
fcharset
|
||||
fclose
|
||||
fcntl
|
||||
fd
|
||||
fdb
|
||||
fdc
|
||||
fdd
|
||||
FDD
|
||||
fde
|
||||
fdopen
|
||||
fdpi
|
||||
fdw
|
||||
fea
|
||||
fesb
|
||||
ffc
|
||||
FFDE
|
||||
FFF
|
||||
ffff
|
||||
FFrom
|
||||
FGCOLOR
|
||||
fgetc
|
||||
@@ -827,7 +767,6 @@ finalizer
|
||||
FINDCASE
|
||||
FINDDLG
|
||||
FINDDOWN
|
||||
findlocalename
|
||||
FINDSTR
|
||||
FINDSTRINGEXACT
|
||||
FINDUP
|
||||
@@ -838,6 +777,7 @@ flyout
|
||||
fmodern
|
||||
fmtarg
|
||||
fmtid
|
||||
fmtlib
|
||||
FOLDERID
|
||||
FONTCHANGE
|
||||
fontdlg
|
||||
@@ -899,7 +839,6 @@ GETALIASESLENGTH
|
||||
GETALIASEXES
|
||||
GETALIASEXESLENGTH
|
||||
GETAUTOHIDEBAREX
|
||||
getboundingrectangles
|
||||
GETCARETWIDTH
|
||||
getch
|
||||
getchar
|
||||
@@ -918,7 +857,6 @@ GETDLGCODE
|
||||
GETDPISCALEDSIZE
|
||||
GETFONTINFO
|
||||
GETFONTSIZE
|
||||
getglyphs
|
||||
GETHARDWARESTATE
|
||||
GETHUNGAPPTIMEOUT
|
||||
GETICON
|
||||
@@ -928,8 +866,6 @@ GETKEYSTATE
|
||||
GETLARGESTWINDOWSIZE
|
||||
GETLBTEXT
|
||||
getline
|
||||
getlocalename
|
||||
getlocalenamelength
|
||||
GETMINMAXINFO
|
||||
GETMOUSEINFO
|
||||
GETNUMBEROFFONTS
|
||||
@@ -939,8 +875,6 @@ GETPOS
|
||||
GETSELECTIONINFO
|
||||
getset
|
||||
GETSTATE
|
||||
getstring
|
||||
getstringlength
|
||||
GETTEXT
|
||||
GETTEXTLEN
|
||||
GETTITLE
|
||||
@@ -952,12 +886,9 @@ GETWHEELSCROLLLINES
|
||||
getwriter
|
||||
Gfun
|
||||
gfx
|
||||
ggggg
|
||||
gggggg
|
||||
ggggggg
|
||||
gh
|
||||
github
|
||||
githubusercontent
|
||||
gitlab
|
||||
gle
|
||||
globals
|
||||
gmail
|
||||
@@ -975,6 +906,7 @@ grep
|
||||
Greyscale
|
||||
gridline
|
||||
groupbox
|
||||
gset
|
||||
gsl
|
||||
GTP
|
||||
guc
|
||||
@@ -986,6 +918,7 @@ GWL
|
||||
GWLP
|
||||
gwsz
|
||||
HABCDEF
|
||||
Hackathon
|
||||
halfwidth
|
||||
HALTCOND
|
||||
HANGEUL
|
||||
@@ -1015,7 +948,6 @@ hhx
|
||||
HIBYTE
|
||||
HICON
|
||||
HIDEWINDOW
|
||||
hidpi
|
||||
HIGHLIGHTTEXT
|
||||
hinst
|
||||
HINSTANCE
|
||||
@@ -1049,9 +981,7 @@ hpp
|
||||
HPR
|
||||
HPROPSHEETPAGE
|
||||
HREDRAW
|
||||
HREF
|
||||
hresult
|
||||
hrgn
|
||||
HRSRC
|
||||
hscroll
|
||||
hsl
|
||||
@@ -1106,9 +1036,6 @@ idllib
|
||||
IDOK
|
||||
IDR
|
||||
idth
|
||||
idwritefontcollection
|
||||
idwritelocalizedstrings
|
||||
idwritetextanalyzer
|
||||
idx
|
||||
IDXGI
|
||||
IDynamic
|
||||
@@ -1134,8 +1061,8 @@ IIo
|
||||
IList
|
||||
ime
|
||||
Imm
|
||||
Impl
|
||||
implementingtextandtextrange
|
||||
IMouse
|
||||
impl
|
||||
inbox
|
||||
inclusivity
|
||||
INCONTEXT
|
||||
@@ -1156,7 +1083,6 @@ inout
|
||||
INPATHROOT
|
||||
Inplace
|
||||
inproc
|
||||
inputdev
|
||||
Inputkeyinfo
|
||||
INPUTPROCESSORPROFILE
|
||||
inputrc
|
||||
@@ -1207,14 +1133,11 @@ iterm
|
||||
itermcolors
|
||||
ITerminal
|
||||
IText
|
||||
itextprovider
|
||||
itextrangeprovider
|
||||
itf
|
||||
Ith
|
||||
itoa
|
||||
IUI
|
||||
IUia
|
||||
iuiautomationtextrange
|
||||
IUnknown
|
||||
ivalid
|
||||
IVector
|
||||
@@ -1244,6 +1167,7 @@ kcub
|
||||
kcud
|
||||
kcuf
|
||||
kcuu
|
||||
Kd
|
||||
keith
|
||||
kernelbase
|
||||
kernelbasestaging
|
||||
@@ -1291,6 +1215,7 @@ LEFTSHIFT
|
||||
len
|
||||
lhs
|
||||
libpopcnt
|
||||
libtickit
|
||||
LIMITTEXT
|
||||
LINEDOWN
|
||||
LINESELECTION
|
||||
@@ -1307,7 +1232,6 @@ listptr
|
||||
listptrsize
|
||||
lk
|
||||
lld
|
||||
LLLLLLLL
|
||||
llvm
|
||||
llx
|
||||
LMENU
|
||||
@@ -1403,6 +1327,7 @@ MAKELONG
|
||||
MAKELPARAM
|
||||
MAKELRESULT
|
||||
malloc
|
||||
manpage
|
||||
MAPBITMAP
|
||||
MAPVIRTUALKEY
|
||||
MAPVK
|
||||
@@ -1426,7 +1351,6 @@ memallocator
|
||||
memcmp
|
||||
memcopy
|
||||
memcpy
|
||||
memcpying
|
||||
memmove
|
||||
memset
|
||||
MENUCHAR
|
||||
@@ -1502,6 +1426,7 @@ Mul
|
||||
multiline
|
||||
munged
|
||||
mutex
|
||||
mutexes
|
||||
muxes
|
||||
myapplet
|
||||
mydir
|
||||
@@ -1522,8 +1447,6 @@ nbsp
|
||||
Nc
|
||||
NCCALCSIZE
|
||||
NCCREATE
|
||||
nchittest
|
||||
NCHITTEST'ed
|
||||
NCLBUTTONDOWN
|
||||
NCLBUTTONUP
|
||||
NCMBUTTONDOWN
|
||||
@@ -1551,13 +1474,10 @@ NEWTEXTMETRIC
|
||||
NEWTEXTMETRICEX
|
||||
Newtonsoft
|
||||
NEXTLINE
|
||||
nf
|
||||
nfe
|
||||
nlength
|
||||
Nls
|
||||
NLSMODE
|
||||
nn
|
||||
nnn
|
||||
NOACTIVATE
|
||||
NOAPPLYNOW
|
||||
NOCLIP
|
||||
@@ -1599,16 +1519,20 @@ NOTHOUSANDS
|
||||
nothrow
|
||||
NOTICKS
|
||||
NOTIMPL
|
||||
notin
|
||||
NOTNULL
|
||||
NOTRACK
|
||||
NOTSUPPORTED
|
||||
notypeopt
|
||||
nouicompat
|
||||
nounihan
|
||||
NOUPDATE
|
||||
NOWAIT
|
||||
NOYIELD
|
||||
NOZORDER
|
||||
NPM
|
||||
npos
|
||||
NRCS
|
||||
NSTATUS
|
||||
ntapi
|
||||
ntcon
|
||||
@@ -1693,6 +1617,7 @@ osfhandle
|
||||
OSG
|
||||
OSGENG
|
||||
osign
|
||||
oss
|
||||
ostream
|
||||
ostringstream
|
||||
Ou
|
||||
@@ -1703,7 +1628,6 @@ Outof
|
||||
OUTOFCONTEXT
|
||||
OUTOFMEMORY
|
||||
outout
|
||||
OUTPATHROOT
|
||||
Outptr
|
||||
Ov
|
||||
OVERLAPPEDWINDOW
|
||||
@@ -1780,7 +1704,6 @@ PFONT
|
||||
PFONTENUMDATA
|
||||
PFS
|
||||
pgdn
|
||||
pgp
|
||||
pguid
|
||||
pgup
|
||||
PHANDLE
|
||||
@@ -1813,9 +1736,7 @@ pntm
|
||||
PNTSTATUS
|
||||
POBJECT
|
||||
Podcast
|
||||
pointerpoint
|
||||
POINTSLIST
|
||||
polytextoutw
|
||||
POLYTEXTW
|
||||
popclip
|
||||
popd
|
||||
@@ -1825,6 +1746,7 @@ popup
|
||||
POPUPATTR
|
||||
PORFLG
|
||||
positionals
|
||||
posix
|
||||
POSTCHARBREAKS
|
||||
POSX
|
||||
POSXSCROLL
|
||||
@@ -1932,7 +1854,6 @@ pythonw
|
||||
qi
|
||||
QJ
|
||||
qo
|
||||
QQQQ
|
||||
qsort
|
||||
queryable
|
||||
QUESTIONMARK
|
||||
@@ -2000,8 +1921,10 @@ REGSTR
|
||||
reingest
|
||||
Relayout
|
||||
RELBINPATH
|
||||
remoting
|
||||
renderengine
|
||||
rendersize
|
||||
reparent
|
||||
reparenting
|
||||
replatformed
|
||||
Replymessage
|
||||
@@ -2054,7 +1977,7 @@ robomac
|
||||
roundtrip
|
||||
ROWSTOSCROLL
|
||||
RRF
|
||||
RRRRRRR
|
||||
RRRGGGBB
|
||||
rtcore
|
||||
RTEXT
|
||||
rtf
|
||||
@@ -2113,6 +2036,7 @@ SCROLLSCALE
|
||||
SCROLLSCREENBUFFER
|
||||
Scrollup
|
||||
Scrolluppage
|
||||
SCS
|
||||
scursor
|
||||
sddl
|
||||
sdeleted
|
||||
@@ -2182,6 +2106,7 @@ Shl
|
||||
shlguid
|
||||
shlobj
|
||||
shlwapi
|
||||
shobjidl
|
||||
SHORTPATH
|
||||
SHOWCURSOR
|
||||
SHOWMAXIMIZED
|
||||
@@ -2222,7 +2147,6 @@ SOURCESDIRECTORY
|
||||
SPACEBAR
|
||||
spammy
|
||||
spand
|
||||
Spc
|
||||
splashscreen
|
||||
sprintf
|
||||
sqlproj
|
||||
@@ -2269,7 +2193,6 @@ stgm
|
||||
stl
|
||||
stoi
|
||||
stol
|
||||
storageitems
|
||||
storelogo
|
||||
stoul
|
||||
stoutapot
|
||||
@@ -2311,7 +2234,6 @@ syscalls
|
||||
SYSCHAR
|
||||
SYSCOMMAND
|
||||
SYSDEADCHAR
|
||||
sysinfo
|
||||
Sysinternals
|
||||
SYSKEYDOWN
|
||||
SYSKEYUP
|
||||
@@ -2345,6 +2267,7 @@ tcommandline
|
||||
tcommands
|
||||
tcon
|
||||
TDP
|
||||
tearoff
|
||||
Teb
|
||||
techcommunity
|
||||
technet
|
||||
@@ -2376,7 +2299,6 @@ TEXCOORD
|
||||
texel
|
||||
textattribute
|
||||
TEXTATTRIBUTEID
|
||||
textblock
|
||||
Textbox
|
||||
textboxes
|
||||
textbuffer
|
||||
@@ -2404,7 +2326,6 @@ TLEN
|
||||
Tlg
|
||||
Tlgdata
|
||||
TMAE
|
||||
tmp
|
||||
TMPF
|
||||
TMult
|
||||
tmultiple
|
||||
@@ -2423,7 +2344,7 @@ TOPDOWNDIB
|
||||
TOPLEFT
|
||||
TOPRIGHT
|
||||
tosign
|
||||
tounicodeex
|
||||
touchpad
|
||||
towlower
|
||||
towupper
|
||||
Tpp
|
||||
@@ -2434,10 +2355,9 @@ tracelogging
|
||||
traceloggingprovider
|
||||
trackbar
|
||||
TRACKCOMPOSITION
|
||||
trackpads
|
||||
trackpad
|
||||
transcoder
|
||||
transitioning
|
||||
trc
|
||||
Trd
|
||||
TREX
|
||||
triaged
|
||||
@@ -2473,8 +2393,11 @@ typeparam
|
||||
TYUI
|
||||
uap
|
||||
uapadmin
|
||||
UAX
|
||||
ubuntu
|
||||
ucd
|
||||
UCD
|
||||
ucdxml
|
||||
uch
|
||||
UCHAR
|
||||
ucs
|
||||
@@ -2485,8 +2408,6 @@ uget
|
||||
uia
|
||||
UIACCESS
|
||||
uiacore
|
||||
uiauto
|
||||
uiautomationclient
|
||||
uiautomationcore
|
||||
Uid
|
||||
uielem
|
||||
@@ -2506,8 +2427,8 @@ unexpand
|
||||
Unfocus
|
||||
unfocuses
|
||||
unhighlighting
|
||||
unhosted
|
||||
unicode
|
||||
unicodechar
|
||||
UNICODESTRING
|
||||
UNICODETEXT
|
||||
UNICRT
|
||||
@@ -2532,6 +2453,7 @@ untimes
|
||||
UPDATEDISPLAY
|
||||
UPDOWN
|
||||
UPKEY
|
||||
UPSS
|
||||
upvote
|
||||
uri
|
||||
url
|
||||
@@ -2560,7 +2482,6 @@ USESHOWWINDOW
|
||||
USESIZE
|
||||
USESTDHANDLES
|
||||
ushort
|
||||
usr
|
||||
USRDLL
|
||||
utf
|
||||
utils
|
||||
@@ -2574,7 +2495,6 @@ uxtheme
|
||||
Vals
|
||||
Vanara
|
||||
vararg
|
||||
varenum
|
||||
vbproj
|
||||
vclib
|
||||
Vcount
|
||||
@@ -2585,7 +2505,6 @@ vcrt
|
||||
vcvarsall
|
||||
vcxitems
|
||||
vcxproj
|
||||
ve
|
||||
vec
|
||||
VERCTRL
|
||||
versioning
|
||||
@@ -2604,7 +2523,6 @@ vkey
|
||||
VKKEYSCAN
|
||||
VMs
|
||||
VPA
|
||||
vpack
|
||||
VPATH
|
||||
VPR
|
||||
VPrintf
|
||||
@@ -2612,7 +2530,6 @@ VProc
|
||||
VRaw
|
||||
VREDRAW
|
||||
vsc
|
||||
vsconfig
|
||||
vscprintf
|
||||
VSCROLL
|
||||
vsinfo
|
||||
@@ -2627,6 +2544,7 @@ VSTT
|
||||
vstudio
|
||||
vswhere
|
||||
vtapp
|
||||
VTE
|
||||
vtio
|
||||
vtmode
|
||||
vtpipeterm
|
||||
@@ -2689,7 +2607,6 @@ wikipedia
|
||||
wil
|
||||
WImpl
|
||||
WINAPI
|
||||
winauto
|
||||
winbase
|
||||
winbasep
|
||||
wincon
|
||||
@@ -2720,10 +2637,8 @@ WINDOWPOSCHANGING
|
||||
windowproc
|
||||
windowrect
|
||||
windowsapp
|
||||
windowsinsiderpreview
|
||||
windowsinternalstring
|
||||
WINDOWSIZE
|
||||
windowsterminal
|
||||
windowsx
|
||||
WINDOWTEXT
|
||||
windowtheme
|
||||
@@ -2731,6 +2646,7 @@ WINDOWTITLE
|
||||
winevent
|
||||
winfx
|
||||
wingdi
|
||||
winget
|
||||
WINIDE
|
||||
winioctl
|
||||
winmd
|
||||
@@ -2778,7 +2694,6 @@ WPresent
|
||||
wprintf
|
||||
wprp
|
||||
wprpi
|
||||
wpt
|
||||
wregex
|
||||
WResult
|
||||
writeback
|
||||
@@ -2788,7 +2703,6 @@ WRITECONSOLEINPUT
|
||||
WRITECONSOLEOUTPUT
|
||||
WRITECONSOLEOUTPUTSTRING
|
||||
wrl
|
||||
wrn
|
||||
wrp
|
||||
WRunoff
|
||||
WScript
|
||||
@@ -2835,7 +2749,6 @@ xca
|
||||
XCast
|
||||
xce
|
||||
XCENTER
|
||||
XChars
|
||||
XColors
|
||||
xcopy
|
||||
XCount
|
||||
@@ -2867,13 +2780,11 @@ xutr
|
||||
xvalue
|
||||
XVIRTUALSCREEN
|
||||
XWalk
|
||||
xxxx
|
||||
XY
|
||||
xy
|
||||
yact
|
||||
YAML
|
||||
YCast
|
||||
YCENTER
|
||||
YChars
|
||||
YCount
|
||||
YDPI
|
||||
yml
|
||||
@@ -2891,6 +2802,7 @@ ZCtrl
|
||||
zd
|
||||
zh
|
||||
ZM
|
||||
zsh
|
||||
zu
|
||||
zxcvbnm
|
||||
zy
|
||||
15
.github/actions/spell-check/expect/web.txt
vendored
Normal file
15
.github/actions/spell-check/expect/web.txt
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
http
|
||||
td
|
||||
www
|
||||
ecma
|
||||
rapidtables
|
||||
WCAG
|
||||
freedesktop
|
||||
ycombinator
|
||||
robertelder
|
||||
kovidgoyal
|
||||
leonerd
|
||||
fixterms
|
||||
uk
|
||||
winui
|
||||
appshellintegration
|
||||
@@ -1,7 +1,18 @@
|
||||
https://(?:(?:[-a-zA-Z0-9?&=]*\.|)microsoft\.com)/[-a-zA-Z0-9?&=_#\/.]*
|
||||
https://aka\.ms/[-a-zA-Z0-9?&=\/_]*
|
||||
https://www\.itscj\.ipsj\.or\.jp/iso-ir/[-0-9]+\.pdf
|
||||
https://www\.vt100\.net/docs/[-a-zA-Z0-9#_\/.]*
|
||||
https://www.w3.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
https://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]*
|
||||
(?:0[Xx]|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]?\b
|
||||
\{[0-9A-FA-F]{8}-(?:[0-9A-FA-F]{4}-){3}[0-9A-FA-F]{12}\}
|
||||
https://[a-z-]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
[Pp]ublicKeyToken="?[0-9a-fA-F]{16}"?
|
||||
(?:[{"]|UniqueIdentifier>)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|</UniqueIdentifier)
|
||||
(?:0[Xx]|\\x|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]{0,2}\b
|
||||
microsoft/cascadia-code\@[0-9a-fA-F]{40}
|
||||
\d+x\d+Logo
|
||||
Scro\&ll
|
||||
# selectionInput.cpp
|
||||
:\\windows\\syste\b
|
||||
TestUtils::VerifyExpectedString\(tb, L"[^"]+"
|
||||
hostSm\.ProcessString\(L"[^"]+"
|
||||
\b([A-Za-z])\1{3,}\b
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
The contents of each `.txt` file in this directory are merged together.
|
||||
|
||||
* [alphabet](alphabet.txt) is a sample for alphabet related items
|
||||
* [web](web.txt) is a sample for web/html related items
|
||||
* [whitelist](whitelist.txt) is the main whitelist -- there is nothing
|
||||
particularly special about the file name (beyond the extension which is
|
||||
important).
|
||||
@@ -1,3 +0,0 @@
|
||||
http
|
||||
td
|
||||
www
|
||||
2
.github/workflows/spelling.yml
vendored
2
.github/workflows/spelling.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- uses: actions/checkout@v2.0.0
|
||||
with:
|
||||
fetch-depth: 5
|
||||
- uses: check-spelling/check-spelling@0.0.12-alpha
|
||||
- uses: check-spelling/check-spelling@0.0.16-alpha
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
bucket: .github/actions
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -48,6 +48,7 @@ dlldata.c
|
||||
project.lock.json
|
||||
artifacts/
|
||||
|
||||
*_h.h
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
|
||||
@@ -125,7 +125,7 @@ Team members will be happy to help review specs and guide them to completion.
|
||||
|
||||
### Help Wanted
|
||||
|
||||
Once the team have approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/terminal/labels/Help-Wanted).
|
||||
Once the team have approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/terminal/labels/Help%20Wanted).
|
||||
|
||||
---
|
||||
|
||||
@@ -155,4 +155,4 @@ Once your code has been reviewed and approved by the requisite number of team me
|
||||
|
||||
## Thank you
|
||||
|
||||
Thank you in advance for your contribution! Now, [what's next on the list](https://github.com/microsoft/terminal/labels/Help-Wanted)? 😜
|
||||
Thank you in advance for your contribution! Now, [what's next on the list](https://github.com/microsoft/terminal/labels/Help%20Wanted)? 😜
|
||||
|
||||
40
NOTICE.md
40
NOTICE.md
@@ -179,4 +179,42 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
## {fmt}
|
||||
|
||||
**Source**: https://github.com/fmtlib/fmt
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2012 - present, Victor Zverovich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
```
|
||||
|
||||
513
OpenConsole.sln
513
OpenConsole.sln
File diff suppressed because it is too large
Load Diff
29
README.md
29
README.md
@@ -2,7 +2,8 @@
|
||||
|
||||
This repository contains the source code for:
|
||||
|
||||
* [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701)
|
||||
* [Windows Terminal](https://aka.ms/terminal)
|
||||
* [Windows Terminal Preview](https://aka.ms/terminal-preview)
|
||||
* The Windows console host (`conhost.exe`)
|
||||
* Components shared between the two projects
|
||||
* [ColorTool](https://github.com/Microsoft/Terminal/tree/master/src/tools/ColorTool)
|
||||
@@ -10,6 +11,7 @@ This repository contains the source code for:
|
||||
|
||||
Related repositories include:
|
||||
|
||||
* [Windows Terminal Documentation](https://docs.microsoft.com/windows/terminal) ([Repo: Contribute to the docs](https://github.com/MicrosoftDocs/terminal))
|
||||
* [Console API Documentation](https://github.com/MicrosoftDocs/Console-Docs)
|
||||
* [Cascadia Code Font](https://github.com/Microsoft/Cascadia-Code)
|
||||
|
||||
@@ -19,7 +21,7 @@ Related repositories include:
|
||||
|
||||
### Microsoft Store [Recommended]
|
||||
|
||||
Install the [Windows Terminal from the Microsoft Store][store-install-link]. This allows you to always be on the latest version when we release new builds with automatic upgrades.
|
||||
Install the [Windows Terminal from the Microsoft Store][store-install-link]. This allows you to always be on the latest version when we release new builds with automatic upgrades.
|
||||
|
||||
This is our preferred method.
|
||||
|
||||
@@ -34,6 +36,14 @@ For users who are unable to install Terminal from the Microsoft Store, Terminal
|
||||
> * Be sure to install the [Desktop Bridge VC++ v14 Redistributable Package](https://www.microsoft.com/en-us/download/details.aspx?id=53175) otherwise Terminal may not install and/or run and may crash at startup
|
||||
> * Terminal will not auto-update when new builds are released so you will need to regularly install the latest Terminal release to receive all the latest fixes and improvements!
|
||||
|
||||
#### Via Windows Package Manager CLI (aka winget)
|
||||
|
||||
[winget](https://github.com/microsoft/winget-cli) users can download and install the latest Terminal release by installing the `Microsoft.WindowsTerminal` package:
|
||||
|
||||
```powershell
|
||||
winget install --id=Microsoft.WindowsTerminal -e
|
||||
```
|
||||
|
||||
#### Via Chocolatey (unofficial)
|
||||
|
||||
[Chocolatey](https://chocolatey.org) users can download and install the latest Terminal release by installing the `microsoft-windows-terminal` package:
|
||||
@@ -52,6 +62,10 @@ If you have any issues when installing/upgrading the package please go to the [W
|
||||
|
||||
---
|
||||
|
||||
## Windows Terminal 2.0 Roadmap
|
||||
|
||||
The plan for delivering Windows Terminal 2.0 [is described here](/doc/terminal-v2-roadmap.md) and will be updated as the project proceeds.
|
||||
|
||||
## Project Build Status
|
||||
|
||||
Project|Build Status
|
||||
@@ -61,12 +75,6 @@ ColorTool|, and will be updated as the project proceeds.
|
||||
|
||||
---
|
||||
|
||||
## Terminal & Console Overview
|
||||
|
||||
Please take a few minutes to review the overview below before diving into the code:
|
||||
@@ -131,7 +139,7 @@ Solution: Make sure you're building & deploying the `CascadiaPackage` project in
|
||||
|
||||
## Documentation
|
||||
|
||||
All project documentation is located in the `./doc` folder. If you would like to contribute to the documentation, please submit a pull request.
|
||||
All project documentation is located at aka.ms/terminal-docs. If you would like to contribute to the documentation, please submit a pull request on the [Windows Terminal Documentation repo](https://github.com/MicrosoftDocs/terminal).
|
||||
|
||||
---
|
||||
|
||||
@@ -150,7 +158,6 @@ Please file new issues, feature requests and suggestions, but **DO search for si
|
||||
If you would like to ask a question that you feel doesn't warrant an issue (yet), please reach out to us via Twitter:
|
||||
|
||||
* Kayla Cinnamon, Program Manager: [@cinnamon\_msft](https://twitter.com/cinnamon_msft)
|
||||
* Rich Turner, Program Manager: [@richturn\_ms](https://twitter.com/richturn_ms)
|
||||
* Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett)
|
||||
* Michael Niksa, Senior Developer: [@michaelniksa](https://twitter.com/MichaelNiksa)
|
||||
* Mike Griese, Developer: [@zadjii](https://twitter.com/zadjii)
|
||||
@@ -227,4 +234,4 @@ For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [open
|
||||
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
|
||||
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
|
||||
[conduct-email]: mailto:opencode@microsoft.com
|
||||
[store-install-link]: https://aka.ms/windowsterminal
|
||||
[store-install-link]: https://aka.ms/terminal
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<SignConfigXML>
|
||||
<job platform="" configuration="" certSubject="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" jobname="EngFunSimpleSign" approvers="">
|
||||
<file src="__INPATHROOT__\Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle" signType="136020001" dest="__OUTPATHROOT__\Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle" />
|
||||
<file src="__INPATHROOT__\Microsoft.WindowsTerminalUniversal_8wekyb3d8bbwe.msixbundle" signType="136020001" dest="__OUTPATHROOT__\Microsoft.WindowsTerminalUniversal_8wekyb3d8bbwe.msixbundle" />
|
||||
<file src="__INPATHROOT__\Microsoft.WindowsTerminal*.msixbundle" signType="136020001" />
|
||||
</job>
|
||||
</SignConfigXML>
|
||||
|
||||
@@ -27,21 +27,42 @@ variables:
|
||||
# 0.0.1904.0900
|
||||
name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
|
||||
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
stages:
|
||||
- stage: Audit_x64
|
||||
displayName: Audit Mode
|
||||
dependsOn: []
|
||||
condition: succeeded()
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
- stage: Build_x64
|
||||
displayName: Build x64
|
||||
dependsOn: []
|
||||
condition: succeeded()
|
||||
jobs:
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
- stage: Build_x86
|
||||
displayName: Build x86
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
- stage: Build_ARM64
|
||||
displayName: Build ARM64
|
||||
dependsOn: []
|
||||
condition: not(eq(variables['Build.Reason'], 'PullRequest'))
|
||||
jobs:
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: ARM64
|
||||
- stage: Scripts
|
||||
displayName: Code Health Scripts
|
||||
dependsOn: []
|
||||
condition: succeeded()
|
||||
jobs:
|
||||
- template: ./templates/check-formatting.yml
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
platform: ARM64
|
||||
|
||||
- template: ./templates/check-formatting.yml
|
||||
|
||||
@@ -15,6 +15,14 @@ variables:
|
||||
# store publication machinery happy.
|
||||
name: 'Terminal_$(date:yyMM).$(date:dd)$(rev:rrr)'
|
||||
|
||||
# Build Arguments:
|
||||
# WindowsTerminalOfficialBuild=[true,false]
|
||||
# true - this is running on our build agent
|
||||
# false - running locally
|
||||
# WindowsTerminalBranding=[Dev,Preview,Release]
|
||||
# <none> - Development build resources (default)
|
||||
# Preview - Preview build resources
|
||||
# Release - regular build resources
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
@@ -23,17 +31,17 @@ jobs:
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
additionalBuildArguments: /p:WindowsTerminalReleaseBuild=true
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
additionalBuildArguments: /p:WindowsTerminalReleaseBuild=true
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: arm64
|
||||
additionalBuildArguments: /p:WindowsTerminalReleaseBuild=true
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
|
||||
- template: ./templates/check-formatting.yml
|
||||
|
||||
|
||||
38
build/rules/CollectWildcardResources.targets
Normal file
38
build/rules/CollectWildcardResources.targets
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="BeforeGenerateProjectPriFile" DependsOnTargets="OpenConsoleCollectWildcardPRIFiles" />
|
||||
|
||||
<!--
|
||||
The vcxproj system does not support wildcards at the root level of a project.
|
||||
This poses a problem, as we want to include resw files that are not checked into the
|
||||
repository. Since they're usually localized and stored in directories named after
|
||||
their languages, we can't exactly explicitly simultaneously list them all and remain
|
||||
sane. We want to use wildcards to make our lives easier.
|
||||
|
||||
This rule takes OCResourceDirectory items and includes all resw files that live
|
||||
underneath them.
|
||||
|
||||
** TIRED **
|
||||
(does not work because of wildcards)
|
||||
<PRIResource Include="Resources/*/Resources.resw" />
|
||||
|
||||
** WIRED **
|
||||
(keep the en-US resource in the project, because it is checked in and VS will show it)
|
||||
<PRIResource Include="Resources/en-US/Resources.resw" />
|
||||
<OCResourceDirectory Include="Resources" />
|
||||
-->
|
||||
<Target Name="OpenConsoleCollectWildcardPRIFiles">
|
||||
<CreateItem Include="@(OCResourceDirectory->'%(Identity)\**\*.resw')">
|
||||
<Output TaskParameter="Include" ItemName="_OCFoundPRIFiles" />
|
||||
</CreateItem>
|
||||
<ItemGroup>
|
||||
<_OCFoundPRIFiles Include="@(PRIResource)" />
|
||||
<PRIResource Remove="@(PRIResource)" />
|
||||
<PRIResource Include="@(_OCFoundPRIFiles->Distinct())" />
|
||||
</ItemGroup>
|
||||
<Message Text="$(ProjectName) (wildcard PRIs) -> @(PRIResource)" />
|
||||
</Target>
|
||||
</Project>
|
||||
14
build/scripts/Generate-ThirdPartyNotices.ps1
Normal file
14
build/scripts/Generate-ThirdPartyNotices.ps1
Normal file
@@ -0,0 +1,14 @@
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Position=0, Mandatory=$true)][string]$MarkdownNoticePath,
|
||||
[Parameter(Position=1, Mandatory=$true)][string]$OutputPath
|
||||
)
|
||||
|
||||
@"
|
||||
<html>
|
||||
<head><title>Third-Party Notices</title></head>
|
||||
<body>
|
||||
$(ConvertFrom-Markdown $MarkdownNoticePath | Select -Expand Html)
|
||||
</body>
|
||||
</html>
|
||||
"@ | Out-File -Encoding UTF-8 $OutputPath -Force
|
||||
@@ -68,6 +68,27 @@ Try {
|
||||
}
|
||||
}
|
||||
|
||||
$dependencies = $Manifest.Package.Dependencies.PackageDependency.Name
|
||||
$depsHasVclibsDesktop = ("Microsoft.VCLibs.140.00.UWPDesktop" -in $dependencies) -or ("Microsoft.VCLibs.140.00.Debug.UWPDesktop" -in $dependencies)
|
||||
$depsHasVcLibsAppX = ("Microsoft.VCLibs.140.00" -in $dependencies) -or ("Microsoft.VCLibs.140.00.Debug" -in $dependencies)
|
||||
$filesHasVclibsDesktop = ($null -ne (Get-Item "$AppxPackageRootPath\vcruntime140.dll" -EA:Ignore)) -or ($null -ne (Get-Item "$AppxPackageRootPath\vcruntime140d.dll" -EA:Ignore))
|
||||
$filesHasVclibsAppX = ($null -ne (Get-Item "$AppxPackageRootPath\vcruntime140_app.dll" -EA:Ignore)) -or ($null -ne (Get-Item "$AppxPackageRootPath\vcruntime140d_app.dll" -EA:Ignore))
|
||||
|
||||
If ($depsHasVclibsDesktop -Eq $filesHasVclibsDesktop) {
|
||||
$eitherBoth = if ($depsHasVclibsDesktop) { "both" } else { "neither" }
|
||||
$neitherNor = if ($depsHasVclibsDesktop) { "and" } else { "nor" }
|
||||
Throw "Package has $eitherBoth Dependency $neitherNor Integrated Desktop VCLibs"
|
||||
}
|
||||
|
||||
If ($depsHasVclibsAppx -Eq $filesHasVclibsAppx) {
|
||||
if ($depsHasVclibsAppx) {
|
||||
# We've shipped like this forever, so downgrade to warning.
|
||||
Write-Warning "Package has both Dependency and Integrated AppX VCLibs"
|
||||
} else {
|
||||
Throw "Package has neither Dependency nor Integrated AppX VCLibs"
|
||||
}
|
||||
}
|
||||
|
||||
### Check that we have an App.xbf (which is a proxy for our resources having been merged)
|
||||
$resourceXpath = '/PriInfo/ResourceMap/ResourceMapSubtree[@name="Files"]/NamedResource[@name="App.xbf"]'
|
||||
$AppXbf = $PRIFile.SelectSingleNode($resourceXpath)
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
".db",
|
||||
".wrn",
|
||||
".rec",
|
||||
".err"
|
||||
".err",
|
||||
".xlsx"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<PropertyGroup Label="Version">
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2020</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>0</VersionMajor>
|
||||
<VersionMinor>11</VersionMinor>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>1</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
4456
dep/CLI11/CLI11.hpp
4456
dep/CLI11/CLI11.hpp
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
# CLI11
|
||||
|
||||
Taken from [release v1.8.0](https://github.com/CLIUtils/CLI11/releases/tag/v1.8.0), source commit
|
||||
[13becad](https://github.com/CLIUtils/CLI11/commit/13becaddb657eacd090537719a669d66d393b8b2)
|
||||
Taken from [release v1.9.0](https://github.com/CLIUtils/CLI11/releases/tag/v1.9.0), source commit
|
||||
[dd0d8e4](https://github.com/CLIUtils/CLI11/commit/dd0d8e4fe729e5b1110232c7a5c9566dad884686)
|
||||
|
||||
|
||||
13
dep/CLI11/cgmanifest.json
Normal file
13
dep/CLI11/cgmanifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{"Registrations":[
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/CLIUtils/CLI11",
|
||||
"commitHash": "dd0d8e4fe729e5b1110232c7a5c9566dad884686"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
@@ -2,6 +2,15 @@
|
||||
|
||||
[Amalgamated](https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated)
|
||||
from source commit
|
||||
[ddabf50](https://github.com/open-source-parsers/jsoncpp/commit/ddabf50f72cf369bf652a95c4d9fe31a1865a781),
|
||||
release 1.8.4.
|
||||
[6aba23f](https://github.com/open-source-parsers/jsoncpp/commit/6aba23f4a8628d599a9ef7fa4811c4ff6e4070e2),
|
||||
release 1.9.3.
|
||||
|
||||
> 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
|
||||
> existing project. The amalgamated source can be generated at any time by
|
||||
> running the following command from the top-directory (this requires Python
|
||||
> 3.4+):
|
||||
>
|
||||
> ```
|
||||
> python amalgamate.py
|
||||
> ```
|
||||
|
||||
13
dep/jsoncpp/cgmanifest.json
Normal file
13
dep/jsoncpp/cgmanifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{"Registrations":[
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/open-source-parsers/jsoncpp",
|
||||
"commitHash": "6aba23f4a8628d599a9ef7fa4811c4ff6e4070e2"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
@@ -79,6 +79,151 @@ license you like.
|
||||
/// to prevent private header inclusion.
|
||||
#define JSON_IS_AMALGAMATION
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// Beginning of content of file: include/json/version.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef JSON_VERSION_H_INCLUDED
|
||||
#define JSON_VERSION_H_INCLUDED
|
||||
|
||||
// Note: version must be updated in three places when doing a release. This
|
||||
// annoying process ensures that amalgamate, CMake, and meson all report the
|
||||
// correct version.
|
||||
// 1. /meson.build
|
||||
// 2. /include/json/version.h
|
||||
// 3. /CMakeLists.txt
|
||||
// IMPORTANT: also update the SOVERSION!!
|
||||
|
||||
#define JSONCPP_VERSION_STRING "1.9.3"
|
||||
#define JSONCPP_VERSION_MAJOR 1
|
||||
#define JSONCPP_VERSION_MINOR 9
|
||||
#define JSONCPP_VERSION_PATCH 3
|
||||
#define JSONCPP_VERSION_QUALIFIER
|
||||
#define JSONCPP_VERSION_HEXA \
|
||||
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
|
||||
(JSONCPP_VERSION_PATCH << 8))
|
||||
|
||||
#ifdef JSONCPP_USING_SECURE_MEMORY
|
||||
#undef JSONCPP_USING_SECURE_MEMORY
|
||||
#endif
|
||||
#define JSONCPP_USING_SECURE_MEMORY 0
|
||||
// If non-zero, the library zeroes any memory that it has allocated before
|
||||
// it frees its memory.
|
||||
|
||||
#endif // JSON_VERSION_H_INCLUDED
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// End of content of file: include/json/version.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// Beginning of content of file: include/json/allocator.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
|
||||
// Distributed under MIT license, or public domain if desired and
|
||||
// recognized in your jurisdiction.
|
||||
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
|
||||
|
||||
#ifndef JSON_ALLOCATOR_H_INCLUDED
|
||||
#define JSON_ALLOCATOR_H_INCLUDED
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#pragma pack(push, 8)
|
||||
|
||||
namespace Json {
|
||||
template <typename T> class SecureAllocator {
|
||||
public:
|
||||
// Type definitions
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
/**
|
||||
* Allocate memory for N items using the standard allocator.
|
||||
*/
|
||||
pointer allocate(size_type n) {
|
||||
// allocate using "global operator new"
|
||||
return static_cast<pointer>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
// free using "global operator delete"
|
||||
::operator delete(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an item in-place at pointer P.
|
||||
*/
|
||||
template <typename... Args> void construct(pointer p, Args&&... args) {
|
||||
// construct using "placement new" and "perfect forwarding"
|
||||
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
size_type max_size() const { return size_t(-1) / sizeof(T); }
|
||||
|
||||
pointer address(reference x) const { return std::addressof(x); }
|
||||
|
||||
const_pointer address(const_reference x) const { return std::addressof(x); }
|
||||
|
||||
/**
|
||||
* Destroy an item in-place at pointer P.
|
||||
*/
|
||||
void destroy(pointer p) {
|
||||
// destroy using "explicit destructor"
|
||||
p->~T();
|
||||
}
|
||||
|
||||
// Boilerplate
|
||||
SecureAllocator() {}
|
||||
template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
|
||||
template <typename U> struct rebind { using other = SecureAllocator<U>; };
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // JSON_ALLOCATOR_H_INCLUDED
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// End of content of file: include/json/allocator.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
// Beginning of content of file: include/json/config.h
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
@@ -90,19 +235,14 @@ license you like.
|
||||
|
||||
#ifndef JSON_CONFIG_H_INCLUDED
|
||||
#define JSON_CONFIG_H_INCLUDED
|
||||
#include <stddef.h>
|
||||
#include <string> //typedef String
|
||||
#include <stdint.h> //typedef int64_t, uint64_t
|
||||
|
||||
/// If defined, indicates that json library is embedded in CppTL library.
|
||||
//# define JSON_IN_CPPTL 1
|
||||
|
||||
/// If defined, indicates that json may leverage CppTL library
|
||||
//# define JSON_USE_CPPTL 1
|
||||
/// If defined, indicates that cpptl vector based map should be used instead of
|
||||
/// std::map
|
||||
/// as Value container.
|
||||
//# define JSON_USE_CPPTL_SMALLMAP 1
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
// If non-zero, the library uses exceptions to report bad input instead of C
|
||||
// assertion macros. The default is to use exceptions.
|
||||
@@ -110,164 +250,132 @@ license you like.
|
||||
#define JSON_USE_EXCEPTION 1
|
||||
#endif
|
||||
|
||||
// Temporary, tracked for removal with issue #982.
|
||||
#ifndef JSON_USE_NULLREF
|
||||
#define JSON_USE_NULLREF 1
|
||||
#endif
|
||||
|
||||
/// If defined, indicates that the source file is amalgamated
|
||||
/// to prevent private header inclusion.
|
||||
/// Remarks: it is automatically defined in the generated amalgamated header.
|
||||
// #define JSON_IS_AMALGAMATION
|
||||
|
||||
#ifdef JSON_IN_CPPTL
|
||||
#include <cpptl/config.h>
|
||||
#ifndef JSON_USE_CPPTL
|
||||
#define JSON_USE_CPPTL 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JSON_IN_CPPTL
|
||||
#define JSON_API CPPTL_API
|
||||
#elif defined(JSON_DLL_BUILD)
|
||||
// Export macros for DLL visibility
|
||||
#if defined(JSON_DLL_BUILD)
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define JSON_API __declspec(dllexport)
|
||||
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define JSON_API __attribute__((visibility("default")))
|
||||
#endif // if defined(_MSC_VER)
|
||||
|
||||
#elif defined(JSON_DLL)
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#define JSON_API __declspec(dllimport)
|
||||
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
|
||||
#endif // if defined(_MSC_VER)
|
||||
#endif // ifdef JSON_IN_CPPTL
|
||||
#endif // ifdef JSON_DLL_BUILD
|
||||
|
||||
#if !defined(JSON_API)
|
||||
#define JSON_API
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1800
|
||||
#error \
|
||||
"ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
// As recommended at
|
||||
// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
|
||||
extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
|
||||
const char* format, ...);
|
||||
#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
|
||||
#else
|
||||
#define jsoncpp_snprintf std::snprintf
|
||||
#endif
|
||||
|
||||
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
|
||||
// integer
|
||||
// Storages, and 64 bits integer support is disabled.
|
||||
// #define JSON_NO_INT64 1
|
||||
|
||||
#if defined(_MSC_VER) // MSVC
|
||||
# if _MSC_VER <= 1200 // MSVC 6
|
||||
// Microsoft Visual Studio 6 only support conversion from __int64 to double
|
||||
// (no conversion from unsigned __int64).
|
||||
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
|
||||
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
|
||||
// characters in the debug information)
|
||||
// All projects I've ever seen with VS6 were using this globally (not bothering
|
||||
// with pragma push/pop).
|
||||
# pragma warning(disable : 4786)
|
||||
# endif // MSVC 6
|
||||
|
||||
# if _MSC_VER >= 1500 // MSVC 2008
|
||||
/// Indicates that the following function is deprecated.
|
||||
# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
|
||||
# endif
|
||||
|
||||
#endif // defined(_MSC_VER)
|
||||
|
||||
// In c++11 the override keyword allows you to explicitly define that a function
|
||||
// is intended to override the base-class version. This makes the code more
|
||||
// managable and fixes a set of common hard-to-find bugs.
|
||||
#if __cplusplus >= 201103L
|
||||
# define JSONCPP_OVERRIDE override
|
||||
# define JSONCPP_NOEXCEPT noexcept
|
||||
#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900
|
||||
# define JSONCPP_OVERRIDE override
|
||||
# define JSONCPP_NOEXCEPT throw()
|
||||
#elif defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
# define JSONCPP_OVERRIDE override
|
||||
# define JSONCPP_NOEXCEPT noexcept
|
||||
#else
|
||||
# define JSONCPP_OVERRIDE
|
||||
# define JSONCPP_NOEXCEPT throw()
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_RVALUE_REFERENCES
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
|
||||
#define JSON_HAS_RVALUE_REFERENCES 1
|
||||
#endif // MSVC >= 2010
|
||||
// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
|
||||
// C++11 should be used directly in JSONCPP.
|
||||
#define JSONCPP_OVERRIDE override
|
||||
|
||||
#ifdef __clang__
|
||||
#if __has_feature(cxx_rvalue_references)
|
||||
#define JSON_HAS_RVALUE_REFERENCES 1
|
||||
#endif // has_feature
|
||||
|
||||
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
|
||||
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
|
||||
#define JSON_HAS_RVALUE_REFERENCES 1
|
||||
#endif // GXX_EXPERIMENTAL
|
||||
|
||||
#endif // __clang__ || __GNUC__
|
||||
|
||||
#endif // not defined JSON_HAS_RVALUE_REFERENCES
|
||||
|
||||
#ifndef JSON_HAS_RVALUE_REFERENCES
|
||||
#define JSON_HAS_RVALUE_REFERENCES 0
|
||||
#if __has_extension(attribute_deprecated_with_message)
|
||||
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
# if __has_extension(attribute_deprecated_with_message)
|
||||
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
|
||||
# endif
|
||||
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
|
||||
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
|
||||
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
|
||||
# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
|
||||
# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
|
||||
# endif // GNUC version
|
||||
#endif // __clang__ || __GNUC__
|
||||
#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
|
||||
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
|
||||
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||
#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
|
||||
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
|
||||
#endif // GNUC version
|
||||
#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates
|
||||
// MSVC)
|
||||
#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
|
||||
#endif // __clang__ || __GNUC__ || _MSC_VER
|
||||
|
||||
#if !defined(JSONCPP_DEPRECATED)
|
||||
#define JSONCPP_DEPRECATED(message)
|
||||
#endif // if !defined(JSONCPP_DEPRECATED)
|
||||
|
||||
#if __GNUC__ >= 6
|
||||
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
|
||||
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
|
||||
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
|
||||
#endif
|
||||
|
||||
#if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
# include "version.h"
|
||||
|
||||
# if JSONCPP_USING_SECURE_MEMORY
|
||||
# include "allocator.h" //typedef Allocator
|
||||
# endif
|
||||
#include "allocator.h"
|
||||
#include "version.h"
|
||||
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
namespace Json {
|
||||
typedef int Int;
|
||||
typedef unsigned int UInt;
|
||||
using Int = int;
|
||||
using UInt = unsigned int;
|
||||
#if defined(JSON_NO_INT64)
|
||||
typedef int LargestInt;
|
||||
typedef unsigned int LargestUInt;
|
||||
using LargestInt = int;
|
||||
using LargestUInt = unsigned int;
|
||||
#undef JSON_HAS_INT64
|
||||
#else // if defined(JSON_NO_INT64)
|
||||
// For Microsoft Visual use specific types as long long is not supported
|
||||
#if defined(_MSC_VER) // Microsoft Visual Studio
|
||||
typedef __int64 Int64;
|
||||
typedef unsigned __int64 UInt64;
|
||||
using Int64 = __int64;
|
||||
using UInt64 = unsigned __int64;
|
||||
#else // if defined(_MSC_VER) // Other platforms, use long long
|
||||
typedef int64_t Int64;
|
||||
typedef uint64_t UInt64;
|
||||
#endif // if defined(_MSC_VER)
|
||||
typedef Int64 LargestInt;
|
||||
typedef UInt64 LargestUInt;
|
||||
using Int64 = int64_t;
|
||||
using UInt64 = uint64_t;
|
||||
#endif // if defined(_MSC_VER)
|
||||
using LargestInt = Int64;
|
||||
using LargestUInt = UInt64;
|
||||
#define JSON_HAS_INT64
|
||||
#endif // if defined(JSON_NO_INT64)
|
||||
#if JSONCPP_USING_SECURE_MEMORY
|
||||
#define JSONCPP_STRING std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char> >
|
||||
#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
|
||||
#define JSONCPP_OSTREAM std::basic_ostream<char, std::char_traits<char>>
|
||||
#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
|
||||
#define JSONCPP_ISTREAM std::istream
|
||||
#else
|
||||
#define JSONCPP_STRING std::string
|
||||
#define JSONCPP_OSTRINGSTREAM std::ostringstream
|
||||
#define JSONCPP_OSTREAM std::ostream
|
||||
#define JSONCPP_ISTRINGSTREAM std::istringstream
|
||||
#define JSONCPP_ISTREAM std::istream
|
||||
#endif // if JSONCPP_USING_SECURE_MEMORY
|
||||
} // end namespace Json
|
||||
|
||||
template <typename T>
|
||||
using Allocator =
|
||||
typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
|
||||
std::allocator<T>>::type;
|
||||
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
|
||||
using IStringStream =
|
||||
std::basic_istringstream<String::value_type, String::traits_type,
|
||||
String::allocator_type>;
|
||||
using OStringStream =
|
||||
std::basic_ostringstream<String::value_type, String::traits_type,
|
||||
String::allocator_type>;
|
||||
using IStream = std::istream;
|
||||
using OStream = std::ostream;
|
||||
} // namespace Json
|
||||
|
||||
// Legacy names (formerly macros).
|
||||
using JSONCPP_STRING = Json::String;
|
||||
using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
|
||||
using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
|
||||
using JSONCPP_ISTREAM = Json::IStream;
|
||||
using JSONCPP_OSTREAM = Json::OStream;
|
||||
|
||||
#endif // JSON_CONFIG_H_INCLUDED
|
||||
|
||||
@@ -299,17 +407,23 @@ typedef UInt64 LargestUInt;
|
||||
namespace Json {
|
||||
|
||||
// writer.h
|
||||
class StreamWriter;
|
||||
class StreamWriterBuilder;
|
||||
class Writer;
|
||||
class FastWriter;
|
||||
class StyledWriter;
|
||||
class StyledStreamWriter;
|
||||
|
||||
// reader.h
|
||||
class Reader;
|
||||
class CharReader;
|
||||
class CharReaderBuilder;
|
||||
|
||||
// features.h
|
||||
// json_features.h
|
||||
class Features;
|
||||
|
||||
// value.h
|
||||
typedef unsigned int ArrayIndex;
|
||||
using ArrayIndex = unsigned int;
|
||||
class StaticString;
|
||||
class Path;
|
||||
class PathArgument;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2
dep/wil
2
dep/wil
Submodule dep/wil updated: e8c599bca6...3c00e7f1d8
@@ -67,12 +67,12 @@ To update the version of a given package, use the following snippet
|
||||
|
||||
where:
|
||||
- `$PackageName` is the name of the package, e.g. Microsoft.UI.Xaml
|
||||
- `$OldVersionNumber` is the version number currently used, e.g. 2.3.191217003-prerelease
|
||||
- `$OldVersionNumber` is the version number currently used, e.g. 2.4.2-prerelease.200604001
|
||||
- `$NewVersionNumber` is the version number you want to migrate to, e.g. 2.4.200117003-prerelease
|
||||
|
||||
Example usage:
|
||||
|
||||
`git grep -z -l Microsoft.UI.Xaml | xargs -0 sed -i -e 's/2.3.191217003-prerelease/2.4.200117003-prerelease/g'`
|
||||
`git grep -z -l Microsoft.UI.Xaml | xargs -0 sed -i -e 's/2.4.2-prerelease.200604001/2.4.200117003-prerelease/g'`
|
||||
|
||||
## Using .nupkg files instead of downloaded Nuget packages
|
||||
If you want to use .nupkg files instead of the downloaded Nuget package, you can do this with the following steps:
|
||||
|
||||
257
doc/cascadia/Json-Utility-API.md
Normal file
257
doc/cascadia/Json-Utility-API.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# New Json Utility API
|
||||
|
||||
## Raw value conversion (GetValue)
|
||||
|
||||
`GetValue` is a convenience helper that will either read a value into existing storage (type-deduced) or
|
||||
return a JSON value coerced into the specified type.
|
||||
|
||||
When reading into existing storage, it returns a boolean indicating whether that storage was modified.
|
||||
|
||||
If the JSON value cannot be converted to the specified type, an exception will be generated.
|
||||
|
||||
```c++
|
||||
std::string one;
|
||||
std::optional<std::string> two;
|
||||
|
||||
JsonUtils::GetValue(json, one);
|
||||
// one is populated or unchanged.
|
||||
|
||||
JsonUtils::GetValue(json, two);
|
||||
// two is populated, nullopt or unchanged
|
||||
|
||||
auto three = JsonUtils::GetValue<std::string>(json);
|
||||
// three is populated or zero-initialized
|
||||
|
||||
auto four = JsonUtils::GetValue<std::optional<std::string>>(json);
|
||||
// four is populated or nullopt
|
||||
```
|
||||
|
||||
## Key lookup (GetValueForKey)
|
||||
|
||||
`GetValueForKey` follows the same rules as `GetValue`, but takes an additional key.
|
||||
It is assumed that the JSON value passed to GetValueForKey is of `object` type.
|
||||
|
||||
```c++
|
||||
std::string one;
|
||||
std::optional<std::string> two;
|
||||
|
||||
JsonUtils::GetValueForKey(json, "firstKey", one);
|
||||
// one is populated or unchanged.
|
||||
|
||||
JsonUtils::GetValueForKey(json, "secondKey", two);
|
||||
// two is populated, nullopt or unchanged
|
||||
|
||||
auto three = JsonUtils::GetValueForKey<std::string>(json, "thirdKey");
|
||||
// three is populated or zero-initialized
|
||||
|
||||
auto four = JsonUtils::GetValueForKey<std::optional<std::string>>(json, "fourthKey");
|
||||
// four is populated or nullopt
|
||||
```
|
||||
|
||||
## Rationale: Value-Returning Getters
|
||||
|
||||
JsonUtils provides two types of `GetValue...`: value-returning and reference-filling.
|
||||
|
||||
The reference-filling fixtures use type deduction so that a developer does not
|
||||
need to specify template parameters on every `GetValue` call. It excels at
|
||||
populating class members during deserialization.
|
||||
|
||||
The value-returning fixtures, on the other hand, are very useful for partial
|
||||
deserialization and key detection when you do not need to deserialize an entire
|
||||
instance of a class or you need to reason about the presence of members.
|
||||
|
||||
To provide a concrete example of the latter, consider:
|
||||
|
||||
```c++
|
||||
if (const auto guid{ GetValueForKey<std::optional<GUID>>(json, "guid") })
|
||||
// This condition is only true if there was a "guid" member in the provided JSON object.
|
||||
// It can be accessed through *guid.
|
||||
}
|
||||
```
|
||||
|
||||
If you are... | Use
|
||||
--------------|-----
|
||||
Deserializing | `GetValue(..., storage)`
|
||||
Interrogating | `storage = GetValue<T>(...)`
|
||||
|
||||
## Converting User-Defined Types
|
||||
|
||||
All conversions are done using specializations of
|
||||
`JsonUtils::ConversionTrait<T>`. To implement a converter for a user-defined
|
||||
type, you must implement a specialization of `JsonUtils::ConversionTrait<T>`.
|
||||
|
||||
Every specialization over `T` must implement `static T FromJson(const Json::Value&)`
|
||||
and `static bool CanConvert(const Json::Value&)`.
|
||||
|
||||
```c++
|
||||
struct MyCustomType { int val; };
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<MyCustomType>
|
||||
{
|
||||
// This trait converts a string of the format "[0-9]" to a value of type MyCustomType.
|
||||
|
||||
static MyCustomType FromJson(const Json::Value& json)
|
||||
{
|
||||
return MyCustomType{ json.asString()[0] - '0' };
|
||||
}
|
||||
|
||||
static bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Converting User-Defined Enumerations
|
||||
|
||||
Enumeration types represent a single choice out of multiple options.
|
||||
|
||||
In a JSON data model, they are typically represented as strings.
|
||||
|
||||
For parsing enumerations, JsonUtils provides the `JSON_ENUM_MAPPER` macro. It
|
||||
can be used to establish a converter that will take a set of known strings and
|
||||
convert them to values.
|
||||
|
||||
```c++
|
||||
JSON_ENUM_MAPPER(CursorStyle)
|
||||
{
|
||||
// pair_type is provided by ENUM_MAPPER.
|
||||
JSON_MAPPINGS(5) = {
|
||||
pair_type{ "bar", CursorStyle::Bar },
|
||||
pair_type{ "vintage", CursorStyle::Vintage },
|
||||
pair_type{ "underscore", CursorStyle::Underscore },
|
||||
pair_type{ "filledBox", CursorStyle::FilledBox },
|
||||
pair_type{ "emptyBox", CursorStyle::EmptyBox }
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
If the enum mapper fails to convert the provided string, it will throw an
|
||||
exception.
|
||||
|
||||
### Converting User-Defined Flag Sets
|
||||
|
||||
Flags represent a multiple-choice selection. They are typically implemented as
|
||||
enums with bitfield values intended to be ORed together.
|
||||
|
||||
In JSON, a set of flags may be represented by a single string (`"flagName"`) or
|
||||
an array of strings (`["flagOne", "flagTwo"]`).
|
||||
|
||||
JsonUtils provides a `JSON_FLAG_MAPPER` macro that can be used to produce a
|
||||
specialization for a set of flags.
|
||||
|
||||
Given the following flag enum,
|
||||
|
||||
```c++
|
||||
enum class JsonTestFlags : int
|
||||
{
|
||||
FlagOne = 1 << 0,
|
||||
FlagTwo = 1 << 1
|
||||
};
|
||||
```
|
||||
|
||||
You can register a flag mapper with the `JSON_FLAG_MAPPER` macro as follows:
|
||||
|
||||
```c++
|
||||
JSON_FLAG_MAPPER(JsonTestFlags)
|
||||
{
|
||||
JSON_MAPPINGS(2) = {
|
||||
pair_type{ "flagOne", JsonTestFlags::FlagOne },
|
||||
pair_type{ "flagTwo", JsonTestFlags::FlagTwo },
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
The `FLAG_MAPPER` also provides two convenience definitions, `AllSet` and
|
||||
`AllClear`, that can be used to represent "all choices" and "no choices"
|
||||
respectively.
|
||||
|
||||
```c++
|
||||
JSON_FLAG_MAPPER(JsonTestFlags)
|
||||
{
|
||||
JSON_MAPPINGS(4) = {
|
||||
pair_type{ "never", AllClear },
|
||||
pair_type{ "flagOne", JsonTestFlags::FlagOne },
|
||||
pair_type{ "flagTwo", JsonTestFlags::FlagTwo },
|
||||
pair_type{ "always", AllSet },
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Because flag values are additive, `["always", "flagOne"]` will result in the
|
||||
same behavior as `"always"`.
|
||||
|
||||
If the flag mapper encounters an unknown flag, it will throw an exception.
|
||||
|
||||
If the flag mapper encounters a logical discontinuity such as `["never", "flagOne"]`
|
||||
(as in the above example), it will throw an exception.
|
||||
|
||||
### Advanced Use
|
||||
|
||||
`GetValue` and `GetValueForKey` can be passed, as their final arguments, any
|
||||
value whose type implements the same interface as `ConversionTrait<T>`--that
|
||||
is, `FromJson(const Json::Value&)` and `CanConvert(const Json::Value&)`.
|
||||
|
||||
This allows for one-off conversions without a specialization of
|
||||
`ConversionTrait` or even stateful converters.
|
||||
|
||||
#### Stateful Converter Sample
|
||||
|
||||
```c++
|
||||
struct MultiplyingConverter {
|
||||
int BaseValue;
|
||||
|
||||
bool CanConvert(const Json::Value&) { return true; }
|
||||
|
||||
int FromJson(const Json::Value& value)
|
||||
{
|
||||
return value.asInt() * BaseValue;
|
||||
}
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
Json::Value json{ 66 }; // A JSON value containing the number 66
|
||||
MultiplyingConverter conv{ 10 };
|
||||
|
||||
auto v = JsonUtils::GetValue<int>(json, conv);
|
||||
// v is equal to 660.
|
||||
```
|
||||
|
||||
## Behavior Chart
|
||||
|
||||
### GetValue(T&) (type-deducing)
|
||||
|
||||
-|json type invalid|json null|valid
|
||||
-|-|-|-
|
||||
`T`|❌ exception|🔵 unchanged|✔ converted
|
||||
`std::optional<T>`|❌ exception|🟨 `nullopt`|✔ converted
|
||||
|
||||
### GetValue<T>() (returning)
|
||||
|
||||
-|json type invalid|json null|valid
|
||||
-|-|-|-
|
||||
`T`|❌ exception|🟨 `T{}` (zero value)|✔ converted
|
||||
`std::optional<T>`|❌ exception|🟨 `nullopt`|✔ converted
|
||||
|
||||
### GetValueForKey(T&) (type-deducing)
|
||||
|
||||
GetValueForKey builds on the behavior set from GetValue by adding
|
||||
a "key not found" state. The remaining three cases are the same.
|
||||
|
||||
val type|key not found|_json type invalid_|_json null_|_valid_
|
||||
-|-|-|-|-
|
||||
`T`|🔵 unchanged|_❌ exception_|_🔵 unchanged_|_✔ converted_
|
||||
`std::optional<T>`|_🔵 unchanged_|_❌ exception_|_🟨 `nullopt`_|_✔ converted_
|
||||
|
||||
### GetValueForKey<T>() (return value)
|
||||
|
||||
val type|key not found|_json type invalid_|_json null_|_valid_
|
||||
-|-|-|-|-
|
||||
`T`|🟨 `T{}` (zero value)|_❌ exception_|_🟨 `T{}` (zero value)_|_✔ converted_
|
||||
`std::optional<T>`|🟨 `nullopt`|_❌ exception_|_🟨 `nullopt`_|_✔ converted_
|
||||
|
||||
### Future Direction
|
||||
|
||||
These converters lend themselves very well to automatic _serialization_.
|
||||
@@ -1,27 +1,34 @@
|
||||
# Profiles.json Documentation
|
||||
# Settings.json Documentation
|
||||
|
||||
## Globals
|
||||
|
||||
Properties listed below affect the entire window, regardless of the profile settings.
|
||||
|
||||
| Property | Necessity | Type | Default | Description |
|
||||
| -------- | --------- | ---- | ------- | ----------- |
|
||||
| `alwaysShowTabs` | _Required_ | Boolean | `true` | When set to `true`, tabs are always displayed. When set to `false` and `showTabsInTitlebar` is set to `false`, tabs only appear after typing <kbd>Ctrl</kbd> + <kbd>T</kbd>. |
|
||||
| `copyOnSelect` | Optional | Boolean | `false` | When set to `true`, a selection is immediately copied to your clipboard upon creation. When set to `false`, the selection persists and awaits further action. |
|
||||
| `copyFormatting` | Optional | Boolean | `false` | When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. |
|
||||
| `defaultProfile` | _Required_ | String | PowerShell guid | Sets the default profile. Opens by typing <kbd>Ctrl</kbd> + <kbd>T</kbd> or by clicking the '+' icon. The guid of the desired default profile is used as the value. |
|
||||
| `initialCols` | _Required_ | Integer | `120` | The number of columns displayed in the window upon first load. |
|
||||
| `initialPosition` | Optional | String | `","` | The position of the top left corner of the window upon first load. On a system with multiple displays, these coordinates are relative to the top left of the primary display. If `launchMode` is set to `"maximized"`, the window will be maximized on the monitor specified by those coordinates. |
|
||||
| `initialRows` | _Required_ | Integer | `30` | The number of rows displayed in the window upon first load. |
|
||||
| `launchMode` | Optional | String | `default` | Defines whether the Terminal will launch as maximized or not. Possible values: `"default"`, `"maximized"` |
|
||||
| `rowsToScroll` | Optional | Integer | `system` | The number of rows to scroll at a time with the mouse wheel. This will override the system setting if the value is not zero or "system". |
|
||||
| `requestedTheme` | _Required_ | String | `system` | Sets the theme of the application. Possible values: `"light"`, `"dark"`, `"system"` |
|
||||
| `theme` | _Required_ | String | `system` | Sets the theme of the application. Possible values: `"light"`, `"dark"`, `"system"` |
|
||||
| `showTerminalTitleInTitlebar` | _Required_ | Boolean | `true` | When set to `true`, titlebar displays the title of the selected tab. When set to `false`, titlebar displays "Windows Terminal". |
|
||||
| `showTabsInTitlebar` | Optional | Boolean | `true` | When set to `true`, the tabs are moved into the titlebar and the titlebar disappears. When set to `false`, the titlebar sits above the tabs. |
|
||||
| `snapToGridOnResize` | Optional | Boolean | `false` | When set to `true`, the window will snap to the nearest character boundary on resize. When `false`, the window will resize "smoothly" |
|
||||
| `tabWidthMode` | Optional | String | `equal` | Sets the width of the tabs. Possible values: `"equal"`, `"titleLength"` |
|
||||
| `tabWidthMode` | Optional | String | `equal` | Sets the width of the tabs. Possible values: <br><ul><li>`"equal"`: sizes each tab to the same width</li><li>`"titleLength"`: sizes each tab to the length of its title</li><li>`"compact"`: sizes each tab to the length of its title when focused, and shrinks to the size of only the icon when the tab is unfocused.</li></ul> |
|
||||
| `wordDelimiters` | Optional | String | <code> /\()"'-:,.;<>~!@#$%^&*|+=[]{}~?│</code><br>_(`│` is `U+2502 BOX DRAWINGS LIGHT VERTICAL`)_ | Determines the delimiters used in a double click selection. |
|
||||
| `confirmCloseAllTabs` | Optional | Boolean | `true` | When set to `true` closing a window with multiple tabs open WILL require confirmation. When set to `false` closing a window with multiple tabs open WILL NOT require confirmation. |
|
||||
| `startOnUserLogin` | Optional | Boolean | `false` | When set to `true` enables the launch of Windows Terminal at startup. Setting to `false` will disable the startup task entry. Note: if the Windows Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect. |
|
||||
| `disabledProfileSources` | Optional | Array[String] | `[]` | Disables all the dynamic profile generators in this list, preventing them from adding their profiles to the list of profiles on startup. This array can contain any combination of `Windows.Terminal.Wsl`, `Windows.Terminal.Azure`, or `Windows.Terminal.PowershellCore`. For more information, see [UsingJsonSettings.md](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingJsonSettings.md#dynamic-profiles) |
|
||||
| `experimental.rendering.forceFullRepaint` | Optional | Boolean | `false` | When set to true, we will redraw the entire screen each frame. When set to false, we will render only the updates to the screen between frames. |
|
||||
| `experimental.rendering.software` | Optional | Boolean | `false` | When set to true, we will use the software renderer (a.k.a. WARP) instead of the hardware one. |
|
||||
|
||||
## Profiles
|
||||
|
||||
Properties listed below are specific to each unique profile.
|
||||
|
||||
| Property | Necessity | Type | Default | Description |
|
||||
@@ -37,29 +44,31 @@ Properties listed below are specific to each unique profile.
|
||||
| `backgroundImageStretchMode` | Optional | String | `uniformToFill` | Sets how the background image is resized to fill the window. Possible values: `"none"`, `"fill"`, `"uniform"`, `"uniformToFill"` |
|
||||
| `closeOnExit` | Optional | String | `graceful` | Sets how the profile reacts to termination or failure to launch. Possible values: `"graceful"` (close when `exit` is typed or the process exits normally), `"always"` (always close) and `"never"` (never close). `true` and `false` are accepted as synonyms for `"graceful"` and `"never"` respectively. |
|
||||
| `colorScheme` | Optional | String | `Campbell` | Name of the terminal color scheme to use. Color schemes are defined under `schemes`. |
|
||||
| `colorTable` | Optional | Array[String] | | Array of colors used in the profile if `colorscheme` is not set. Array follows the format defined in `schemes`. |
|
||||
| `commandline` | Optional | String | | Executable used in the profile. |
|
||||
| `cursorColor` | Optional | String | | Sets the cursor color of the profile. Overrides `cursorColor` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `cursorHeight` | Optional | Integer | | Sets the percentage height of the cursor starting from the bottom. Only works when `cursorShape` is set to `"vintage"`. Accepts values from 25-100. |
|
||||
| `cursorShape` | Optional | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( ▃ ), `"bar"` ( ┃ ), `"underscore"` ( ▁ ), `"filledBox"` ( █ ), `"emptyBox"` ( ▯ ) |
|
||||
| `fontFace` | Optional | String | `Cascadia Code` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. |
|
||||
| `fontFace` | Optional | String | `Cascadia Mono` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. |
|
||||
| `fontSize` | Optional | Integer | `12` | Sets the font size. |
|
||||
| `fontWeight` | Optional | String | `normal` | Sets the weight (lightness or heaviness of the strokes) for the given font. Possible values: `"thin"`, `"extra-light"`, `"light"`, `"semi-light"`, `"normal"`, `"medium"`, `"semi-bold"`, `"bold"`, `"extra-bold"`, `"black"`, `"extra-black"`, or the corresponding numeric representation of OpenType font weight. |
|
||||
| `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `#rgb` or `"#rrggbb"`. |
|
||||
| `hidden` | Optional | Boolean | `false` | If set to true, the profile will not appear in the list of profiles. This can be used to hide default profiles and dynamically generated profiles, while leaving them in your settings file. |
|
||||
| `historySize` | Optional | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. |
|
||||
| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. |
|
||||
| `padding` | Optional | String | `8, 8, 8, 8` | Sets the padding around the text within the window. Can have three different formats: `"#"` sets the same padding for all sides, `"#, #"` sets the same padding for left-right and top-bottom, and `"#, #, #, #"` sets the padding individually for left, top, right, and bottom. |
|
||||
| `scrollbarState` | Optional | String | | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` |
|
||||
| `scrollbarState` | Optional | String | `"visible"` | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` |
|
||||
| `selectionBackground` | Optional | String | | Sets the selection background color of the profile. Overrides `selectionBackground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `snapOnInput` | Optional | Boolean | `true` | When set to `true`, the window will scroll to the command input line when typing. When set to `false`, the window will not scroll when you start typing. |
|
||||
| `altGrAliasing` | Optional | Boolean | `true` | By default Windows treats Ctrl+Alt as an alias for AltGr. When altGrAliasing is set to false, this behavior will be disabled. |
|
||||
| `source` | Optional | String | | Stores the name of the profile generator that originated this profile. _There are no discoverable values for this field._ |
|
||||
| `startingDirectory` | Optional | String | `%USERPROFILE%` | The directory the shell starts in when it is loaded. |
|
||||
| `suppressApplicationTitle` | Optional | Boolean | | 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. |
|
||||
| `suppressApplicationTitle` | Optional | Boolean | `false` | 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. |
|
||||
| `tabTitle` | Optional | String | | If set, will replace the `name` as the title to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. |
|
||||
| `useAcrylic` | Optional | Boolean | `false` | When set to `true`, the window will have an acrylic background. When set to `false`, the window will have a plain, untextured background. The transparency only applies to focused windows due to OS limitation. |
|
||||
| `experimental.retroTerminalEffect` | Optional | Boolean | `false` | When set to `true`, enable retro terminal effects. This is an experimental feature, and its continued existence is not guaranteed. |
|
||||
|
||||
## Schemes
|
||||
|
||||
Properties listed below are specific to each color scheme. [ColorTool](https://github.com/microsoft/terminal/tree/master/src/tools/ColorTool) is a great tool you can use to create and explore new color schemes. All colors use hex color format.
|
||||
|
||||
| Property | Necessity | Type | Description |
|
||||
@@ -87,12 +96,13 @@ Properties listed below are specific to each color scheme. [ColorTool](https://g
|
||||
| `yellow` | _Required_ | String | Sets the color used as ANSI yellow. |
|
||||
|
||||
## Keybindings
|
||||
|
||||
Properties listed below are specific to each custom key binding.
|
||||
|
||||
| Property | Necessity | Type | Description |
|
||||
| -------- | ---- | ----------- | ----------- |
|
||||
| `command` | _Required_ | String | The command executed when the associated key bindings are pressed. |
|
||||
| `keys` | _Required_ | Array[String] | Defines the key combinations used to call the command. |
|
||||
| `keys` | _Required_ | Array[String] or String | Defines the key combinations used to call the command. |
|
||||
| `action` | Optional | String | Adds additional functionality to certain commands. |
|
||||
|
||||
### Implemented Commands and Actions
|
||||
@@ -111,14 +121,13 @@ For commands with arguments:
|
||||
|
||||
| Command | Command Description | Action (*=required) | Action Arguments | Argument Descriptions |
|
||||
| ------- | ------------------- | ------ | ---------------- | ----------------- |
|
||||
| `adjustFontSize` | Change the text size by a specified point amount. | `delta` | integer | Amount of size change per command invocation. |
|
||||
| `closePane` | Close the active pane. | | | |
|
||||
| `closeTab` | Close the current tab. | | | |
|
||||
| `closeWindow` | Close the current window and all tabs within it. | | | |
|
||||
| `copy` | Copy the selected terminal content to your Windows Clipboard. | `trimWhitespace` | boolean | When `true`, newlines persist from the selected text. When `false`, copied content will paste on one line. |
|
||||
| `decreaseFontSize` | Make the text smaller by one delta. | `delta` | integer | Amount of size decrease per command invocation. |
|
||||
| `copy` | Copy the selected terminal content to your Windows Clipboard. | `singleLine` | boolean | When `true`, the copied content will be copied as a single line. When `false`, newlines persist from the selected text. |
|
||||
| `duplicateTab` | Make a copy and open the current tab. | | | |
|
||||
| `find` | Open the search dialog box. | | | |
|
||||
| `increaseFontSize` | Make the text larger by one delta. | `delta` | integer | Amount of size increase per command invocation. |
|
||||
| `moveFocus` | Focus on a different pane depending on direction. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the focus will move. |
|
||||
| `newTab` | Create a new tab. Without any arguments, this will open the default profile in a new tab. | 1. `commandLine`<br>2. `startingDirectory`<br>3. `tabTitle`<br>4. `index`<br>5. `profile` | 1. string<br>2. string<br>3. string<br>4. integer<br>5. string | 1. Executable run within the tab.<br>2. Directory in which the tab will open.<br>3. Title of the new tab.<br>4. Profile that will open based on its position in the dropdown (starting at 0).<br>5. Profile that will open based on its GUID or name. |
|
||||
| `nextTab` | Open the tab to the right of the current one. | | | |
|
||||
@@ -132,7 +141,7 @@ For commands with arguments:
|
||||
| `scrollUp` | Move the screen up. | | | |
|
||||
| `scrollUpPage` | Move the screen up a whole page. | | | |
|
||||
| `scrollDownPage` | Move the screen down a whole page. | | | |
|
||||
| `splitPane` | Halve the size of the active pane and open another. Without any arguments, this will open the default profile in the new pane. | 1. `split`*<br>2. `commandLine`<br>3. `startingDirectory`<br>4. `tabTitle`<br>5. `index`<br>6. `profile` | 1. `vertical`, `horizontal`, `auto`<br>2. string<br>3. string<br>4. string<br>5. integer<br>6. string | 1. How the pane will split. `auto` will split in the direction that provides the most surface area.<br>2. Executable run within the pane.<br>3. Directory in which the pane will open.<br>4. Title of the tab when the new pane is focused.<br>5. Profile that will open based on its position in the dropdown (starting at 0).<br>6. Profile that will open based on its GUID or name. |
|
||||
| `splitPane` | Halve the size of the active pane and open another. Without any arguments, this will open the default profile in the new pane. | 1. `split`*<br>2. `commandLine`<br>3. `startingDirectory`<br>4. `tabTitle`<br>5. `index`<br>6. `profile`<br>7. `splitMode` | 1. `vertical`, `horizontal`, `auto`<br>2. string<br>3. string<br>4. string<br>5. integer<br>6. string<br>7. string | 1. How the pane will split. `auto` will split in the direction that provides the most surface area.<br>2. Executable run within the pane.<br>3. Directory in which the pane will open.<br>4. Title of the tab when the new pane is focused.<br>5. Profile that will open based on its position in the dropdown (starting at 0).<br>6. Profile that will open based on its GUID or name.<br>7. Controls how the pane splits. Only accepts `duplicate` which will duplicate the focused pane's profile into a new pane. |
|
||||
| `switchToTab` | Open a specific tab depending on index. | `index`* | integer | Tab that will open based on its position in the tab bar (starting at 0). |
|
||||
| `toggleFullscreen` | Switch between fullscreen and default window sizes. | | | |
|
||||
| `unbound` | Unbind the associated keys from any command. | | | |
|
||||
@@ -140,9 +149,10 @@ For commands with arguments:
|
||||
### Accepted Modifiers and Keys
|
||||
|
||||
#### Modifiers
|
||||
`Ctrl+`, `Shift+`, `Alt+`
|
||||
`ctrl+`, `shift+`, `alt+`
|
||||
|
||||
#### Keys
|
||||
|
||||
| Type | Keys |
|
||||
| ---- | ---- |
|
||||
| Function and Alphanumeric Keys | `f1-f24`, `a-z`, `0-9` |
|
||||
@@ -152,6 +162,7 @@ For commands with arguments:
|
||||
| Numpad Keys | `numpad_0-numpad_9`, `numpad0-numpad9`, `numpad_add`, `numpad_plus`, `numpad_decimal`, `numpad_period`, `numpad_divide`, `numpad_minus`, `numpad_subtract`, `numpad_multiply` |
|
||||
|
||||
## Background Images and Icons
|
||||
|
||||
Some Terminal settings allow you to specify custom background images and icons. It is recommended that custom images and icons are stored in system-provided folders and are referred to using the correct [URI Schemes](https://docs.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes). URI Schemes provide a way to reference files independent of their physical paths (which may change in the future).
|
||||
|
||||
The most useful URI schemes to remember when customizing background images and icons are:
|
||||
@@ -164,6 +175,7 @@ The most useful URI schemes to remember when customizing background images and i
|
||||
> ⚠ Note: Do not rely on file references using the `ms-appx` URI Scheme (i.e. icons). These files are considered an internal implementation detail and may change name/location or may be omitted in the future.
|
||||
|
||||
### Icons
|
||||
|
||||
Terminal displays icons for each of your profiles which Terminal generates for any built-in shells - PowerShell Core, PowerShell, and any installed Linux/WSL distros. Each profile refers to a stock icon via the `ms-appx` URI Scheme.
|
||||
|
||||
> ⚠ Note: Do not rely on the files referenced by the `ms-appx` URI Scheme - they are considered an internal implementation detail and may change name/location or may be omitted in the future.
|
||||
@@ -177,6 +189,7 @@ You can refer to you own icons if you wish, e.g.:
|
||||
> 👉 Tip: Icons should be sized to 32x32px in an appropriate raster image format (e.g. .PNG, .GIF, or .ICO) to avoid having to scale your icons during runtime (causing a noticeable delay and loss of quality.)
|
||||
|
||||
### Custom Background Images
|
||||
|
||||
You can apply a background image to each of your profiles, allowing you to configure/brand/style each of your profiles independently from one another if you wish.
|
||||
|
||||
To do so, specify your preferred `backgroundImage`, position it using `backgroundImageAlignment`, set its opacity with `backgroundImageOpacity`, and/or specify how your image fill the available space using `backgroundImageStretchMode`.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$id": "https://github.com/microsoft/terminal/blob/master/doc/cascadia/profiles.schema.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Microsoft's Windows Terminal Settings Profile Schema'",
|
||||
"title": "Microsoft's Windows Terminal Settings Profile Schema",
|
||||
"definitions": {
|
||||
"KeyChordSegment": {
|
||||
"pattern": "^(?<modifier>(ctrl|alt|shift)(?:\\+(ctrl|alt|shift)(?<!\\2))?(?:\\+(ctrl|alt|shift)(?<!\\2|\\3))?\\+)?(?<key>[^\\s+]|backspace|tab|enter|esc|escape|space|pgup|pageup|pgdn|pagedown|end|home|left|up|right|down|insert|delete|(?<!shift.+)(?:numpad_?[0-9]|numpad_(?:period|decimal))|numpad_(?:multiply|plus|add|minus|subtract|divide)|f[1-9]|f1[0-9]|f2[0-4]|plus)$",
|
||||
@@ -18,6 +18,14 @@
|
||||
"pattern": "^(-?\\d+)?(,\\s?(-?\\d+)?)?$",
|
||||
"type": "string"
|
||||
},
|
||||
"DynamicProfileSource": {
|
||||
"enum": [
|
||||
"Windows.Terminal.Wsl",
|
||||
"Windows.Terminal.Azure",
|
||||
"Windows.Terminal.PowershellCore"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ProfileGuid": {
|
||||
"default": "{}",
|
||||
"pattern": "^\\{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\\}$",
|
||||
@@ -25,29 +33,14 @@
|
||||
},
|
||||
"ShortcutActionName": {
|
||||
"enum": [
|
||||
"adjustFontSize",
|
||||
"closePane",
|
||||
"closeTab",
|
||||
"closeWindow",
|
||||
"copy",
|
||||
"copyTextWithoutNewlines",
|
||||
"decreaseFontSize",
|
||||
"duplicateTab",
|
||||
"increaseFontSize",
|
||||
"moveFocus",
|
||||
"moveFocusDown",
|
||||
"moveFocusLeft",
|
||||
"moveFocusRight",
|
||||
"moveFocusUp",
|
||||
"newTab",
|
||||
"newTabProfile0",
|
||||
"newTabProfile1",
|
||||
"newTabProfile2",
|
||||
"newTabProfile3",
|
||||
"newTabProfile4",
|
||||
"newTabProfile5",
|
||||
"newTabProfile6",
|
||||
"newTabProfile7",
|
||||
"newTabProfile8",
|
||||
"nextTab",
|
||||
"openNewTabDropdown",
|
||||
"openSettings",
|
||||
@@ -55,29 +48,15 @@
|
||||
"prevTab",
|
||||
"resetFontSize",
|
||||
"resizePane",
|
||||
"resizePaneDown",
|
||||
"resizePaneLeft",
|
||||
"resizePaneRight",
|
||||
"resizePaneUp",
|
||||
"scrollDown",
|
||||
"scrollDownPage",
|
||||
"scrollUp",
|
||||
"scrollUpPage",
|
||||
"splitHorizontal",
|
||||
"splitVertical",
|
||||
"splitPane",
|
||||
"switchToTab",
|
||||
"switchToTab0",
|
||||
"switchToTab1",
|
||||
"switchToTab2",
|
||||
"switchToTab3",
|
||||
"switchToTab4",
|
||||
"switchToTab5",
|
||||
"switchToTab6",
|
||||
"switchToTab7",
|
||||
"switchToTab8",
|
||||
"toggleFullscreen",
|
||||
"find"
|
||||
"find",
|
||||
"unbound"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -118,7 +97,7 @@
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"description": "The index of the profile in the new tab dropdown to open"
|
||||
"description": "The index of the profile in the new tab dropdown (starting at 0)"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -135,6 +114,23 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AdjustFontSizeAction": {
|
||||
"description": "Arguments corresponding to an Adjust Font Size Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "adjustFontSize" },
|
||||
"delta": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "How much to change the current font point size"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "delta" ]
|
||||
},
|
||||
"CopyAction": {
|
||||
"description": "Arguments corresponding to a Copy Text Action",
|
||||
"allOf": [
|
||||
@@ -142,10 +138,10 @@
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "copy" },
|
||||
"trimWhitespace": {
|
||||
"singleLine": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "If true, whitespace is removed and newlines are maintained. If false, newlines are removed and whitespace is maintained."
|
||||
"default": false,
|
||||
"description": "If true, the copied content will be copied as a single line (even if there are hard line breaks present in the text). If false, newlines persist from the selected text."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,12 +221,41 @@
|
||||
"split": {
|
||||
"$ref": "#/definitions/SplitState",
|
||||
"default": "auto",
|
||||
"description": "The orientation to split the pane in, either vertical (think [|]), horizontal (think [-]), or auto (splits pane based on remaining space)"
|
||||
"description": "The orientation to split the pane in. Possible values:\n -\"auto\" (splits pane based on remaining space)\n -\"horizontal\" (think [-])\n -\"vertical\" (think [|])"
|
||||
},
|
||||
"splitMode": {
|
||||
"default": "duplicate",
|
||||
"description": "Control how the pane splits. Only accepts \"duplicate\" which will duplicate the focused pane's profile into a new pane."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "split" ]
|
||||
]
|
||||
},
|
||||
"OpenSettingsAction": {
|
||||
"description": "Arguments corresponding to a Open Settings Action",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ShortcutAction"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"pattern": "openSettings"
|
||||
},
|
||||
"target": {
|
||||
"type": "string",
|
||||
"default": "settingsFile",
|
||||
"description": "The settings file to open.",
|
||||
"enum": [
|
||||
"settingsFile",
|
||||
"defaultsFile",
|
||||
"allFiles"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
@@ -238,17 +263,20 @@
|
||||
"command": {
|
||||
"description": "The action executed when the associated key bindings are pressed.",
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/AdjustFontSizeAction" },
|
||||
{ "$ref": "#/definitions/CopyAction" },
|
||||
{ "$ref": "#/definitions/ShortcutActionName" },
|
||||
{ "$ref": "#/definitions/NewTabAction" },
|
||||
{ "$ref": "#/definitions/SwitchToTabAction" },
|
||||
{ "$ref": "#/definitions/MoveFocusAction" },
|
||||
{ "$ref": "#/definitions/ResizePaneAction" },
|
||||
{ "$ref": "#/definitions/SplitPaneAction" }
|
||||
{ "$ref": "#/definitions/SplitPaneAction" },
|
||||
{ "$ref": "#/definitions/OpenSettingsAction" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
"keys": {
|
||||
"description": "Defines the key combinations used to call the command.",
|
||||
"description": "Defines the key combinations used to call the command. It must be composed of...\n -any number of modifiers (ctrl/alt/shift)\n -a non-modifier key",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeyChordSegment"
|
||||
@@ -275,7 +303,7 @@
|
||||
"properties": {
|
||||
"alwaysShowTabs": {
|
||||
"default": true,
|
||||
"description": "When set to true, tabs are always displayed. When set to false and showTabsInTitlebar is set to false, tabs only appear after opening a new tab.",
|
||||
"description": "When set to true, tabs are always displayed. When set to false and \"showTabsInTitlebar\" is set to false, tabs only appear after opening a new tab.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyOnSelect": {
|
||||
@@ -283,9 +311,29 @@
|
||||
"description": "When set to true, a selection is immediately copied to your clipboard upon creation. When set to false, the selection persists and awaits further action.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyFormatting": {
|
||||
"default": true,
|
||||
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"defaultProfile": {
|
||||
"$ref": "#/definitions/ProfileGuid",
|
||||
"description": "Sets the default profile. Opens by clicking the '+' icon or typing the key binding assigned to 'newTab'. The guid of the desired default profile is used as the value."
|
||||
"description": "Sets the default profile. Opens by clicking the \"+\" icon or typing the key binding assigned to \"newTab\".",
|
||||
"type": "string"
|
||||
},
|
||||
"disabledProfileSources": {
|
||||
"description": "Disables all the dynamic profile generators in this list, preventing them from adding their profiles to the list of profiles on startup.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/DynamicProfileSource"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"experimental.rendering.forceFullRepaint": {
|
||||
"description": "When set to true, we will redraw the entire screen each frame. When set to false, we will render only the updates to the screen between frames.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.rendering.software": {
|
||||
"description": "When set to true, we will use the software renderer (a.k.a. WARP) instead of the hardware one.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"initialCols": {
|
||||
"default": 120,
|
||||
@@ -296,7 +344,7 @@
|
||||
},
|
||||
"initialPosition": {
|
||||
"$ref": "#/definitions/Coordinates",
|
||||
"description": "The position of the top left corner of the window upon first load. On a system with multiple displays, these coordinates are relative to the top left of the primary display. If launchMode is set to maximized, the window will be maximized on the monitor specified by those coordinates."
|
||||
"description": "The position of the top left corner of the window upon first load. On a system with multiple displays, these coordinates are relative to the top left of the primary display. If \"launchMode\" is set to maximized, the window will be maximized on the monitor specified by those coordinates."
|
||||
},
|
||||
"initialRows": {
|
||||
"default": 30,
|
||||
@@ -316,10 +364,10 @@
|
||||
},
|
||||
"rowsToScroll": {
|
||||
"default": "system",
|
||||
"description": "The number of rows to scroll at a time with the mouse wheel. This will override the system setting if the value is not zero or 'system'.",
|
||||
"description": "The number of rows to scroll at a time with the mouse wheel. This will override the system setting if the value is not zero or \"system\".",
|
||||
"maximum": 999,
|
||||
"minimum": 0,
|
||||
"type": ["integer", "string"]
|
||||
"type": [ "integer", "string" ]
|
||||
},
|
||||
"keybindings": {
|
||||
"description": "Properties are specific to each custom key binding.",
|
||||
@@ -328,9 +376,9 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"requestedTheme": {
|
||||
"theme": {
|
||||
"default": "system",
|
||||
"description": "Sets the theme of the application.",
|
||||
"description": "Sets the theme of the application. The special value \"system\" refers to the active Windows system theme.",
|
||||
"enum": [
|
||||
"light",
|
||||
"dark",
|
||||
@@ -345,18 +393,19 @@
|
||||
},
|
||||
"showTerminalTitleInTitlebar": {
|
||||
"default": true,
|
||||
"description": "When set to true, titlebar displays the title of the selected tab. When set to false, titlebar displays 'Windows Terminal'.",
|
||||
"description": "When set to true, titlebar displays the title of the selected tab. When set to false, titlebar displays \"Windows Terminal\".",
|
||||
"type": "boolean"
|
||||
},
|
||||
"snapToGridOnResize": {
|
||||
"default": false,
|
||||
"description": "When set to true, the window will snap to the nearest character boundary on resize. When false, the window will resize 'smoothly'",
|
||||
"description": "When set to true, the window will snap to the nearest character boundary on resize. When false, the window will resize smoothly",
|
||||
"type": "boolean"
|
||||
},
|
||||
"tabWidthMode": {
|
||||
"default": "equal",
|
||||
"description": "Sets the width of the tabs.",
|
||||
"description": "Sets the width of the tabs. Possible values include:\n -\"equal\" sizes each tab to the same width\n -\"titleLength\" sizes each tab to the length of its title\n -\"compact\" sizes each tab to the length of its title when focused, and shrinks to the size of only the icon when the tab is unfocused.",
|
||||
"enum": [
|
||||
"compact",
|
||||
"equal",
|
||||
"titleLength"
|
||||
],
|
||||
@@ -369,8 +418,8 @@
|
||||
},
|
||||
"confirmCloseAllTabs": {
|
||||
"default": true,
|
||||
"description": " When set to `true` closing a window with multiple tabs open WILL require confirmation. When set to `false` closing a window with multiple tabs open WILL NOT require confirmation.",
|
||||
"type":"boolean"
|
||||
"description": "When set to \"true\" closing a window with multiple tabs open will require confirmation. When set to \"false\", the confirmation dialog will not appear.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -402,12 +451,12 @@
|
||||
"background": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": "#0c0c0c",
|
||||
"description": "Sets the background color of the profile. Overrides background set in color scheme if colorscheme is set. Uses hex color format: \"#rrggbb\".",
|
||||
"description": "Sets the background color of the text. Overrides \"background\" from the color scheme. Uses hex color format: \"#rrggbb\".",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"backgroundImage": {
|
||||
"description": "Sets the file location of the Image to draw over the window background.",
|
||||
"type": "string"
|
||||
"description": "Sets the file location of the image to draw over the window background.",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"backgroundImageAlignment": {
|
||||
"default": "center",
|
||||
@@ -422,12 +471,14 @@
|
||||
"topLeft",
|
||||
"topRight"
|
||||
],
|
||||
"description": "Sets how the background image aligns to the boundaries of the window. Possible values: \"center\", \"left\", \"top\", \"right\", \"bottom\", \"topLeft\", \"topRight\", \"bottomLeft\", \"bottomRight\"",
|
||||
"type": "string"
|
||||
},
|
||||
"backgroundImageOpacity": {
|
||||
"description": "(Not in SettingsSchema.md)",
|
||||
"maximum": 1,
|
||||
"minimum": 0,
|
||||
"default": 1.0,
|
||||
"description": "Sets the transparency of the background image. Accepts floating point values from 0-1.",
|
||||
"maximum": 1.0,
|
||||
"minimum": 0.0,
|
||||
"type": "number"
|
||||
},
|
||||
"backgroundImageStretchMode": {
|
||||
@@ -443,7 +494,7 @@
|
||||
},
|
||||
"closeOnExit": {
|
||||
"default": "graceful",
|
||||
"description": "Sets how the profile reacts to termination or failure to launch. Possible values: \"graceful\" (close when exit is typed or the process exits normally), \"always\" (always close) and \"never\" (never close). true and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",
|
||||
"description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
@@ -463,109 +514,27 @@
|
||||
"description": "Name of the terminal color scheme to use. Color schemes are defined under \"schemes\".",
|
||||
"type": "string"
|
||||
},
|
||||
"colorTable": {
|
||||
"description": "Array of colors used in the profile if colorscheme is not set. Colors use hex color format: \"#rrggbb\". Ordering is as follows: [black, red, green, yellow, blue, magenta, cyan, white, bright black, bright red, bright green, bright yellow, bright blue, bright magenta, bright cyan, bright white]",
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"background": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the background color of the color table."
|
||||
},
|
||||
"black": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI black."
|
||||
},
|
||||
"blue": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI blue."
|
||||
},
|
||||
"brightBlack": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright black."
|
||||
},
|
||||
"brightBlue": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright blue."
|
||||
},
|
||||
"brightCyan": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright cyan."
|
||||
},
|
||||
"brightGreen": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright green."
|
||||
},
|
||||
"brightPurple": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright purple."
|
||||
},
|
||||
"brightRed": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright red."
|
||||
},
|
||||
"brightWhite": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright white."
|
||||
},
|
||||
"brightYellow": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright yellow."
|
||||
},
|
||||
"cyan": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI cyan."
|
||||
},
|
||||
"foreground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the foreground color of the color table."
|
||||
},
|
||||
"green": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI green."
|
||||
},
|
||||
"purple": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI purple."
|
||||
},
|
||||
"red": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI red."
|
||||
},
|
||||
"white": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI white."
|
||||
},
|
||||
"yellow": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI yellow."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"commandline": {
|
||||
"description": "Executable used in the profile.",
|
||||
"type": "string"
|
||||
},
|
||||
"connectionType": {
|
||||
"$ref": "#/definitions/ProfileGuid",
|
||||
"description": "A GUID reference to a connection type. Currently undocumented as of 0.3, this is used for Azure Cloud Shell"
|
||||
},
|
||||
"cursorColor": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the cursor color of the profile. Overrides cursor color set in color scheme if colorscheme is set. Uses hex color format: \"#rrggbb\"."
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/Color" },
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "Sets the color of the cursor. Overrides the cursor color from the color scheme. Uses hex color format: \"#rrggbb\"."
|
||||
},
|
||||
"cursorHeight": {
|
||||
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 25-100.",
|
||||
"maximum": 100,
|
||||
"minimum": 25,
|
||||
"type": "integer"
|
||||
"type": ["integer","null"],
|
||||
"default": 25
|
||||
},
|
||||
"cursorShape": {
|
||||
"default": "bar",
|
||||
"description": "Sets the cursor shape for the profile. Possible values: \"vintage\" ( ▃ ), \"bar\" ( ┃, default ), \"underscore\" ( ▁ ), \"filledBox\" ( █ ), \"emptyBox\" ( ▯ )",
|
||||
"description": "Sets the shape of the cursor. Possible values:\n -\"bar\" ( ┃, default )\n -\"emptyBox\" ( ▯ )\n -\"filledBox\" ( █ )\n -\"underscore\" ( ▁ )\n -\"vintage\" ( ▃ )",
|
||||
"enum": [
|
||||
"bar",
|
||||
"emptyBox",
|
||||
@@ -580,20 +549,47 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"fontFace": {
|
||||
"default": "Cascadia Code",
|
||||
"default": "Cascadia Mono",
|
||||
"description": "Name of the font face used in the profile.",
|
||||
"type": "string"
|
||||
},
|
||||
"fontSize": {
|
||||
"default": 12,
|
||||
"description": "Sets the font size.",
|
||||
"description": "Size of the font in points.",
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"fontWeight": {
|
||||
"default": "normal",
|
||||
"description": "Sets the weight (lightness or heaviness of the strokes) for the given font. Possible values:\n -\"thin\"\n -\"extra-light\"\n -\"light\"\n -\"semi-light\"\n -\"normal\" (default)\n -\"medium\"\n -\"semi-bold\"\n -\"bold\"\n -\"extra-bold\"\n -\"black\"\n -\"extra-black\" or the corresponding numeric representation of OpenType font weight.",
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"thin",
|
||||
"extra-light",
|
||||
"light",
|
||||
"semi-light",
|
||||
"normal",
|
||||
"medium",
|
||||
"semi-bold",
|
||||
"bold",
|
||||
"extra-bold",
|
||||
"black",
|
||||
"extra-black"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"maximum": 990,
|
||||
"minimum": 100,
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"foreground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": "#cccccc",
|
||||
"description": "Sets the foreground color of the profile. Overrides foreground set in color scheme if colorscheme is set. Uses hex color format: \"#rrggbb\".",
|
||||
"description": "Sets the text color. Overrides \"foreground\" from the color scheme. Uses hex color format: \"#rrggbb\".",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"guid": {
|
||||
@@ -613,7 +609,7 @@
|
||||
},
|
||||
"icon": {
|
||||
"description": "Image file location of the icon used in the profile. Displays within the tab and the dropdown menu.",
|
||||
"type": "string"
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of the profile. Displays in the dropdown menu.",
|
||||
@@ -622,7 +618,7 @@
|
||||
},
|
||||
"padding": {
|
||||
"default": "8, 8, 8, 8",
|
||||
"description": "Sets the padding around the text within the window. Can have three different formats: \"#\" sets the same padding for all sides, \"#, #\" sets the same padding for left-right and top-bottom, and \"#, #, #, #\" sets the padding individually for left, top, right, and bottom.",
|
||||
"description": "Sets the padding around the text within the window. Can have three different formats:\n -\"#\" sets the same padding for all sides \n -\"#, #\" sets the same padding for left-right and top-bottom\n -\"#, #, #, #\" sets the padding individually for left, top, right, and bottom.",
|
||||
"pattern": "^-?[0-9]+(\\.[0-9]+)?( *, *-?[0-9]+(\\.[0-9]+)?|( *, *-?[0-9]+(\\.[0-9]+)?){3})?$",
|
||||
"type": "string"
|
||||
},
|
||||
@@ -636,17 +632,25 @@
|
||||
"type": "string"
|
||||
},
|
||||
"selectionBackground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the selection background color of the profile. Overrides selection background set in color scheme if colorscheme is set. Uses hex color format: \"#rrggbb\"."
|
||||
"oneOf": [
|
||||
{"$ref": "#/definitions/Color"},
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "Sets the background color of selected text. Overrides selectionBackground set in the color scheme. Uses hex color format: \"#rrggbb\"."
|
||||
},
|
||||
"snapOnInput": {
|
||||
"default": true,
|
||||
"description": "When set to true, the window will scroll to the command input line when typing. When set to false, the window will not scroll when you start typing.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"altGrAliasing": {
|
||||
"default": true,
|
||||
"description": "By default Windows treats Ctrl+Alt as an alias for AltGr. When altGrAliasing is set to false, this behavior will be disabled.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"source": {
|
||||
"description": "Stores the name of the profile generator that originated this profile.",
|
||||
"type": "string"
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"startingDirectory": {
|
||||
"description": "The directory the shell starts in when it is loaded.",
|
||||
@@ -654,11 +658,12 @@
|
||||
},
|
||||
"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.",
|
||||
"type": "boolean"
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"tabTitle": {
|
||||
"description": "If set, will replace the name as the title to pass to the shell on startup. Some shells (like bash) may choose to ignore this initial value, while others (cmd, powershell) may use this value over the lifetime of the application.",
|
||||
"type": "string"
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"useAcrylic": {
|
||||
"default": false,
|
||||
@@ -789,37 +794,15 @@
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/Globals" },
|
||||
{
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/Globals" },
|
||||
{
|
||||
"additionalItems": true,
|
||||
"properties": {
|
||||
"profiles": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/ProfileList" },
|
||||
{ "$ref": "#/definitions/ProfilesObject" }
|
||||
]
|
||||
},
|
||||
"schemes": { "$ref": "#/definitions/SchemeList" }
|
||||
},
|
||||
"required": [
|
||||
"profiles",
|
||||
"schemes",
|
||||
"defaultProfile"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"additionalItems": false,
|
||||
"additionalItems": true,
|
||||
"properties": {
|
||||
"globals": { "$ref": "#/definitions/Globals" },
|
||||
"profiles": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/ProfileList" },
|
||||
{ "$ref": "#/definitions/ProfilesObject" }
|
||||
{ "$ref": "#/definitions/ProfileList" },
|
||||
{ "$ref": "#/definitions/ProfilesObject" }
|
||||
]
|
||||
},
|
||||
"schemes": { "$ref": "#/definitions/SchemeList" }
|
||||
@@ -827,7 +810,7 @@
|
||||
"required": [
|
||||
"profiles",
|
||||
"schemes",
|
||||
"globals"
|
||||
"defaultProfile"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
BIN
doc/reference/customtextlayout.xlsx
Normal file
BIN
doc/reference/customtextlayout.xlsx
Normal file
Binary file not shown.
788
doc/specs/#2046 - Command Palette.md
Normal file
788
doc/specs/#2046 - Command Palette.md
Normal file
@@ -0,0 +1,788 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2019-08-01
|
||||
last updated: 2020-06-10
|
||||
issue id: 2046
|
||||
---
|
||||
|
||||
# Command Palette
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec covers the addition of a "command palette" to the Windows Terminal.
|
||||
The Command Palette is a GUI that the user can activate to search for and
|
||||
execute commands. Beneficially, the command palette allows the user to execute
|
||||
commands _even if they aren't bound to a keybinding_.
|
||||
|
||||
## Inspiration
|
||||
|
||||
This feature is largely inspired by the "Command Palette" in text editors like
|
||||
VsCode, Sublime Text and others.
|
||||
|
||||
This spec was initially drafted in [a
|
||||
comment](https://github.com/microsoft/terminal/issues/2046#issuecomment-514219791)
|
||||
in [#2046]. That was authored during the annual Microsoft Hackathon, where I
|
||||
proceeded to prototype the solution. This spec is influenced by things I learned
|
||||
prototyping.
|
||||
|
||||
Initially, the command palette was designed simply as a method for executing
|
||||
certain actions that the user pre-defined. With the addition of [commandline
|
||||
arguments](https://github.com/microsoft/terminal/issues/4632) to the Windows
|
||||
Terminal in v0.9, we also considered what it might mean to be able to have the
|
||||
command palette work as an effective UI not only for dispatching pre-defined
|
||||
commands, but also `wt.exe` commandlines to the current terminal instance.
|
||||
|
||||
## Solution Design
|
||||
|
||||
Fundamentally, we need to address two different modes of using the command palette:
|
||||
* In the first mode, the command palette can be used to quickly look up
|
||||
pre-defined actions and dispatch them. We'll refer to this as "Action Mode".
|
||||
* The second mode allows the user to run `wt` commandline commands and have them
|
||||
apply immediately to the current Terminal window. We'll refer to this as
|
||||
"commandline mode".
|
||||
|
||||
Both these options will be discussed in detail below.
|
||||
|
||||
### Action Mode
|
||||
|
||||
We'll introduce a new top-level array to the user settings, under the key
|
||||
`commands`. `commands` will contain an array of commands, each with the
|
||||
following schema:
|
||||
|
||||
```js
|
||||
{
|
||||
"name": string|object,
|
||||
"action": string|object,
|
||||
"icon": string
|
||||
}
|
||||
```
|
||||
|
||||
Command names should be human-friendly names of actions, though they don't need
|
||||
to necessarily be related to the action that it fires. For example, a command
|
||||
with `newTab` as the action could have `"Open New Tab"` as the name.
|
||||
|
||||
The command will be parsed into a new class, `Command`:
|
||||
|
||||
```c++
|
||||
class Command
|
||||
{
|
||||
winrt::hstring Name();
|
||||
winrt::TerminalApp::ActionAndArgs ActionAndArgs();
|
||||
winrt::hstring IconSource();
|
||||
}
|
||||
```
|
||||
|
||||
We'll add another structure in GlobalAppSettings to hold all these actions. It
|
||||
will just be a `std::vector<Command>` in `GlobalAppSettings`.
|
||||
|
||||
We'll need app to be able to turn this vector into a `ListView`, or similar, so
|
||||
that we can display this list of actions. Each element in the view will be
|
||||
intrinsically associated with the `Command` object it's associated with. In
|
||||
order to support this, we'll make `Command` a winrt type that implements
|
||||
`Windows.UI.Xaml.Data.INotifyPropertyChanged`. This will let us bind the XAML
|
||||
element to the winrt type.
|
||||
|
||||
When an element is clicked on in the list of commands, we'll raise the event
|
||||
corresponding to that `ShortcutAction`. `AppKeyBindings` already does a great
|
||||
job of dispatching `ShortcutActions` (and their associated arguments), so we'll
|
||||
re-use that. We'll pull the basic parts of dispatching `ActionAndArgs`
|
||||
callbacks into another class, `ShortcutActionDispatch`, with a single
|
||||
`DoAction(ActionAndArgs)` method (and events for each action).
|
||||
`AppKeyBindings` will be initialized with a reference to the
|
||||
`ShortcutActionDispatch` object, so that it can call `DoAction` on it.
|
||||
Additionally, by having a singular `ShortcutActionDispatch` instance, we won't
|
||||
need to re-hook up the ShortcutAction keybindings each time we re-load the
|
||||
settings.
|
||||
|
||||
In `TerminalPage`, when someone clicks on an item in the list, we'll get the
|
||||
`ActionAndArgs` associated with that list element, and call `DoAction` on
|
||||
the app's `ShortcutActionDispatch`. This will trigger the event handler just the
|
||||
same as pressing the keybinding.
|
||||
|
||||
#### Commands for each profile?
|
||||
|
||||
[#3879] Is a request for being able to launch a profile directly, via the
|
||||
command palette. Essentially, the user will type the name of a profile, and hit
|
||||
enter to launch that profile. I quite like this idea, but with the current spec,
|
||||
this won't work great. We'd need to manually have one entry in the command
|
||||
palette for each profile, and every time the user adds a profile, they'd need to
|
||||
update the list of commands to add a new entry for that profile as well.
|
||||
|
||||
This is a fairly complicated addition to this feature, so I'd hold it for
|
||||
"Command Palette v2", though I believe it's solution deserves special
|
||||
consideration from the outset.
|
||||
|
||||
I suggest that we need a mechanism by which the user can specify a single
|
||||
command that would be expanded to one command for every profile in the list of
|
||||
profiles. Consider the following sample:
|
||||
|
||||
```json
|
||||
"commands": [
|
||||
{
|
||||
"expandOn": "profiles",
|
||||
"icon": "${profile.icon}",
|
||||
"name": "New Tab with ${profile.name}",
|
||||
"command": { "action": "newTab", "profile": "${profile.name}" }
|
||||
},
|
||||
{
|
||||
"expandOn": "profiles",
|
||||
"icon": "${profile.icon}",
|
||||
"name": "New Vertical Split with ${profile.name}",
|
||||
"command": { "action": "splitPane", "split":"vertical", "profile": "${profile.name}" }
|
||||
}
|
||||
],
|
||||
```
|
||||
|
||||
In this example:
|
||||
* The `"expandOn": "profiles"` property indicates that each command should be
|
||||
repeated for each individual profile.
|
||||
* The `${profile.name}` value is treated as "when expanded, use the given
|
||||
profile's name". This allows each command to use the `name` and `icon`
|
||||
properties of a `Profile` to customize the text of the command.
|
||||
|
||||
To ensure that this works correctly, we'll need to make sure to expand these
|
||||
commands after all the other settings have been parsed, presumably in the
|
||||
`Validate` phase. If we do it earlier, it's possible that not all the profiles
|
||||
from various sources will have been added yet, which would lead to an incomplete
|
||||
command list.
|
||||
|
||||
We'll need to have a placeholder property to indicate that a command should be
|
||||
expanded for each `Profile`. When the command is first parsed, we'll leave the
|
||||
format strings `${...}` unexpanded at this time. Then, in the validate phase,
|
||||
when we encounter a `"expandOn": "profiles"` command, we'll remove it from the
|
||||
list, and use it as a prototype to generate commands for every `Profile` in our
|
||||
profiles list. We'll do a string find-and-replace on the format strings to
|
||||
replace them with the values from the profile, before adding the completed
|
||||
command to the list of commands.
|
||||
|
||||
Of course, how does this work with localization? Considering the [section
|
||||
below](#localization), we'd update the built-in commands to the following:
|
||||
|
||||
```json
|
||||
"commands": [
|
||||
{
|
||||
"iterateOn": "profiles",
|
||||
"icon": "${profile.icon}",
|
||||
"name": { "key": "NewTabWithProfileCommandName" },
|
||||
"command": { "action": "newTab", "profile": "${profile.name}" }
|
||||
},
|
||||
{
|
||||
"iterateOn": "profiles",
|
||||
"icon": "${profile.icon}",
|
||||
"name": { "key": "NewVerticalSplitWithProfileCommandName" },
|
||||
"command": { "action": "splitPane", "split":"vertical", "profile": "${profile.name}" }
|
||||
}
|
||||
],
|
||||
```
|
||||
|
||||
In this example, we'll look up the `NewTabWithProfileCommandName` resource when
|
||||
we're first parsing the command, to find a string similar to `"New Tab with
|
||||
${profile.name}"`. When we then later expand the command, we'll see the
|
||||
`${profile.name}` bit from the resource, and expand that like we normally would.
|
||||
|
||||
Trickily, we'll need to make sure to have a helper for replacing strings like
|
||||
this that can be used for general purpose arg parsing. As you can see, the
|
||||
`profile` property of the `newTab` command also needs the name of the profile.
|
||||
Either the command validation will need to go through and update these strings
|
||||
manually, or we'll need another of enabling these `IActionArgs` classes to fill
|
||||
those parameters in based on the profile being used. Perhaps the command
|
||||
pre-expansion could just stash the json for the action, then expand it later?
|
||||
This implementation detail is why this particular feature is not slated for
|
||||
inclusion in an initial Command Palette implementation.
|
||||
|
||||
From initial prototyping, it seems like the best solution will be to stash the
|
||||
command's original json around when parsing an expandable command like the above
|
||||
examples. Then, we'll handle the expansion in the settings validation phase,
|
||||
after all the profiles and color schemes have been loaded.
|
||||
|
||||
For each profile, we'll need to replace all the instances in the original json
|
||||
of strings like `${profile.name}` with the profile's name to create a new json
|
||||
string. We'll attempt to parse that new string into a new command to add to the
|
||||
list of commands.
|
||||
|
||||
### Commandline Mode
|
||||
|
||||
One of our more highly requested features is the ability to run a `wt.exe`
|
||||
commandline in the current WT window (see [#4472]). Typically, users want the
|
||||
ability to do this straight from whatever shell they're currently running.
|
||||
However, we don't really have an effective way currently to know if WT is itself
|
||||
being called from another WT instance, and passing those arguments to the
|
||||
hosting WT. Furthermore, in the long term, we see that feature as needing the
|
||||
ability to not only run commands in the current WT window, but an _arbitrary_ WT
|
||||
window.
|
||||
|
||||
The Command Palette seems like a natural fit for a stopgap measure while we
|
||||
design the correct way to have a `wt` commandline apply to the window it's
|
||||
running in.
|
||||
|
||||
In Commandline Mode, the user can simply type a `wt.exe` commandline, and when
|
||||
they hit enter, we'll parse the commandline and dispatch it _to the current
|
||||
window_. So if the user wants to open a new tab, they could type `new-tab` in
|
||||
Commandline Mode, and it would open a new tab in the current window. They're
|
||||
also free to chain multiple commands like they can with `wt` from a shell - by
|
||||
entering something like `split-pane -p "Windows PowerShell" ; split-pane -H
|
||||
wsl.exe`, the terminal would execute two `SplitPane` actions in the currently
|
||||
focused pane, creating one with the "Windows PowerShell" profile and another
|
||||
with the default profile running `wsl` in it.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
We'll add another action that can be used to toggle the visibility of the
|
||||
command palette. Pressing that keybinding will bring up the command palette. We
|
||||
should make sure to add a argument to this action that specifies whether the
|
||||
palette should be opened directly in Action Mode or Commandline Mode.
|
||||
|
||||
When the command palette appears, we'll want it to appear as a single overlay
|
||||
over all of the panes of the Terminal. The drop-down will be centered
|
||||
horizontally, dropping down from the top (from the tab row). When commands are
|
||||
entered, it will be implied that they are delivered to the focused terminal
|
||||
pane. This will help avoid two problematic scenarios that could arise from
|
||||
having the command palette attached to a single pane:
|
||||
* When attached to a single pane, it might be very easy for the UI to quickly
|
||||
become cluttered, especially at smaller pane sizes.
|
||||
* This avoids the "find the overlay problem" which is common in editors like
|
||||
VS where the dialog appears attached to the active editor pane.
|
||||
|
||||
The palette will consist of two main UI elements: a text box for
|
||||
entering/searching for commands, and in action mode, a list of commands.
|
||||
|
||||
### Action Mode
|
||||
|
||||
The list of commands will be populated with all the commands by default. Each
|
||||
command will appear like a `MenuFlyoutItem`, with an icon at the left (if it has
|
||||
one) and the name visible. When opened, the palette will automatically highlight
|
||||
the first entry in the list.
|
||||
|
||||
The user can navigate the list of entries with the arrow keys. Hitting enter
|
||||
will close the palette and execute the action that's highlighted. Hitting escape
|
||||
will dismiss the palette, returning control to the terminal. When the palette is
|
||||
closed for any reason (executing a command, dismissing with either escape or the
|
||||
`toggleCommandPalette` keybinding), we'll clear out any search text from the
|
||||
palette, so the user can start fresh again.
|
||||
|
||||
We'll also want to enable the command palette to be filterable, so that the user
|
||||
can type the name of a command, and the command palette will automatically
|
||||
filter the list of commands. This should be more powerful then just a simple
|
||||
string compare - the user should be able to type a search string, and get all
|
||||
the commands that match a "fuzzy search" for that string. This will allow users
|
||||
to find the command they're looking for without needing to type the entire
|
||||
command.
|
||||
|
||||
For example, consider the following list of commands:
|
||||
|
||||
```json
|
||||
"commands": [
|
||||
{ "icon": null, "name": "New Tab", "action": "newTab" },
|
||||
{ "icon": null, "name": "Close Tab", "action": "closeTab" },
|
||||
{ "icon": null, "name": "Close Pane", "action": "closePane" },
|
||||
{ "icon": null, "name": "[-] Split Horizontal", "action": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "icon": null, "name": "[ | ] Split Vertical", "action": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "icon": null, "name": "Next Tab", "action": "nextTab" },
|
||||
{ "icon": null, "name": "Prev Tab", "action": "prevTab" },
|
||||
{ "icon": null, "name": "Open Settings", "action": "openSettings" },
|
||||
{ "icon": null, "name": "Open Media Controls", "action": "openTestPane" }
|
||||
],
|
||||
```
|
||||
|
||||
* "open" should return both "**Open** Settings" and "**Open** Media Controls".
|
||||
* "Tab" would return "New **Tab**", "Close **Tab**", "Next **Tab**" and "Prev
|
||||
**Tab**".
|
||||
* "P" would return "Close **P**ane", "[-] S**p**lit Horizontal", "[ | ]
|
||||
S**p**lit Vertical", "**P**rev Tab", "O**p**en Settings" and "O**p**en Media
|
||||
Controls".
|
||||
* Even more powerfully, "sv" would return "[ | ] Split Vertical" (by matching
|
||||
the **S** in "Split", then the **V** in "Vertical"). This is a great example
|
||||
of how a user could execute a command with very few keystrokes.
|
||||
|
||||
As the user types, we should **bold** each matching character in the command
|
||||
name, to show how their input correlates to the results on screen.
|
||||
|
||||
Additionally, it will be important for commands in the action list to display
|
||||
the keybinding that's bound to them, if there is one.
|
||||
|
||||
### Commandline Mode
|
||||
|
||||
Commandline mode is much simpler. In this mode, we'll simply display a text input,
|
||||
similar to the search box that's rendered for Action Mode. In this box, the
|
||||
user will be able to type a `wt.exe` style commandline. The user does not need
|
||||
to start this commandline with `wt` (or `wtd`, etc) - since we're already
|
||||
running in WT, the user shouldn't really need to repeat themselves.
|
||||
|
||||
When the user hits <kbd>enter</kbd>, we'll attempt to parse the commandline. If
|
||||
we're successful in parsing the commandline, we can close the palette and
|
||||
dispatch the commandline. If the commandline had errors, we should reveal a text
|
||||
box with an error message below the text input. We'll leave the palette open
|
||||
with their entered command, so they can edit the commandline and try again. We
|
||||
should _probably_ leave the message up for a few seconds once they've begun
|
||||
editing the commandline, but eventually hide the message (ideally with a motion
|
||||
animation).
|
||||
|
||||
### Switching Between Modes
|
||||
|
||||
**TODO**: This is a topic for _discussion_.
|
||||
|
||||
How do we differentiate Action Mode from Commandline Mode?
|
||||
|
||||
I think there should be a character that the user types that switches the mode.
|
||||
This is reminiscent of how the command palette works in applications like VsCode
|
||||
and Sublime Text. The same UI is used for a number of functions. In the case of
|
||||
VsCode, when the user opens the palette, it's initially in a "navigate to file"
|
||||
mode. When the user types the prefix character `@`, the menu seamlessly switches
|
||||
to a "navigate to symbol mode". Similarly, users can use `:` for "go to line"
|
||||
and `>` enters an "editor command" mode.
|
||||
|
||||
I believe we should use a similarly implemented UI. The UI would be in one of
|
||||
the two modes by default, and typing the prefix character would enter the other
|
||||
mode. If the user deletes the prefix character, then we'd switch back into the
|
||||
default mode.
|
||||
|
||||
When the user is in Action Mode vs Commandline mode, if the input is empty
|
||||
(besides potentially the prefix character), we should probably have some sort of
|
||||
placeholder text visible to indicate which mode the user is in. Something like
|
||||
_"Enter a command name..."_ for action mode, or _"Type a wt commandline..."_ for
|
||||
commandline mode.
|
||||
|
||||
Initially, I favored having the palette in Action Mode by default, and typing a
|
||||
`:` prefix to enter Commandline Mode. This is fairly similar to how tmux's
|
||||
internal command prompt works, which is bound to `<prefix>-:` by default.
|
||||
|
||||
If we wanted to remain _similar_ to VsCode, we'd have no prefix character be the
|
||||
Commandline Mode, and `>` would enter the Action mode. I'd think that might
|
||||
actually be _backwards_ from what I'd expect, with `>` being the default
|
||||
character for the end of the default `cmd` `%PROMPT%`.
|
||||
|
||||
**FOR DISCUSSION** What option makes the most sense to the team? I'm leaning
|
||||
towards the VsCode style (where Action='>', Commandline='') currently.
|
||||
|
||||
Enabling the user to configure this prefix is discussed below in "[Future
|
||||
Considerations](#Configuring-The-ActionCommandline-Mode-Prefix)".
|
||||
|
||||
### Layering and "Unbinding" Commands
|
||||
|
||||
As we'll be providing a list of default commands, the user will inevitably want
|
||||
to change or remove some of these default commands.
|
||||
|
||||
Commands should be layered based upon the _evaluated_ value of the "name"
|
||||
property. Since the default commands will all use localized strings in the
|
||||
`"name": { "key": "KeyName" }` format, the user should be able to override the
|
||||
command based on the localized string for that command.
|
||||
|
||||
So, assuming that `NewTabCommandName` is evaluated as "Open New Tab", the
|
||||
following command
|
||||
```json
|
||||
{ "icon": null, "name": { "key": "NewTabCommandName" }, "action": "newTab" },
|
||||
```
|
||||
|
||||
Could be overridden with the command:
|
||||
```json
|
||||
{ "icon": null, "name": "Open New Tab", "action": "splitPane" },
|
||||
```
|
||||
|
||||
Similarly, if the user wants to remove that command from the command palette,
|
||||
they could set the action to `null`:
|
||||
|
||||
```json
|
||||
{ "icon": null, "name": "Open New Tab", "action": null },
|
||||
```
|
||||
|
||||
This will remove the command from the command list.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
As the entire command palette will be a native XAML element, it'll automatically
|
||||
be hooked up to the UIA tree, allowing for screen readers to naturally find it.
|
||||
* When the palette is opened, it will automatically receive focus.
|
||||
* The terminal panes will not be able to be interacted with while the palette
|
||||
is open, which will help keep the UIA tree simple while the palette is open.
|
||||
|
||||
### Security
|
||||
|
||||
This should not introduce any _new_ security concerns. We're relying on the
|
||||
security of jsoncpp for parsing json. Adding new keys to the settings file
|
||||
will rely on jsoncpp's ability to securely parse those json values.
|
||||
|
||||
### Reliability
|
||||
|
||||
We'll need to make sure that invalid commands are ignored. A command could be
|
||||
invalid because:
|
||||
* it has a null `name`, or a name with the empty string for a value.
|
||||
* it has a null `action`, or an action specified that's not an actual
|
||||
`ShortcutAction`.
|
||||
|
||||
We'll ignore invalid commands from the user's settings, instead of hard
|
||||
crashing. I don't believe this is a scenario that warrants an error dialog to
|
||||
indicate to the user that there's a problem with the json.
|
||||
|
||||
### Compatibility
|
||||
|
||||
We will need to define default _commands_ for all the existing keybinding
|
||||
commands. With #754, we could add all the actions (that make sense) as commands
|
||||
to the commands list, so that everyone wouldn't need to define them manually.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
We'll be adding a few extra XAML elements to our tree which will certainly
|
||||
increase our runtime memory footprint while the palette is open.
|
||||
|
||||
We'll additionally be introducing a few extra json values to parse, so that could
|
||||
increase our load times (though this will likely be negligible).
|
||||
|
||||
## Potential Issues
|
||||
|
||||
This will first require the work in [#1205] to work properly. Right now we
|
||||
heavily lean on the "focused" element to determine which terminal is "active".
|
||||
However, when the command palette is opened, focus will move out of the terminal
|
||||
control into the command palette, which leads to some hard to debug crashes.
|
||||
|
||||
Additionally, we'll need to ensure that the "fuzzy search" algorithm proposed
|
||||
above will work for non-english languages, where a single character might be
|
||||
multiple `char`s long. As we'll be using a standard XAML text box for input, we
|
||||
won't need to worry about handling the input ourselves.
|
||||
|
||||
### Localization
|
||||
|
||||
Because we'll be shipping a set of default commands with the terminal, we should
|
||||
make sure that list of commands can be localizable. Each of the names we'll give
|
||||
to the commands should be locale-specific.
|
||||
|
||||
To facilitate this, we'll use a special type of object in JSON that will let us
|
||||
specify a resource name in JSON. We'll use a syntax like the following to
|
||||
suggest that we should load a string from our resources, as opposed to using the
|
||||
value from the file:
|
||||
|
||||
```json
|
||||
"commands": [
|
||||
{ "icon": null, "name": { "key": "NewTabCommandName" }, "action": "newTab" },
|
||||
{ "icon": null, "name": { "key": "CloseTabCommandKey" }, "action": "closeTab" },
|
||||
{ "icon": null, "name": { "key": "ClosePaneCommandKey" }, "action": "closePane" },
|
||||
{ "icon": null, "name": { "key": "SplitHorizontalCommandKey" }, "action": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "icon": null, "name": { "key": "SplitVerticalCommandKey" }, "action": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "icon": null, "name": { "key": "NextTabCommandKey" }, "action": "nextTab" },
|
||||
{ "icon": null, "name": { "key": "PrevTabCommandKey" }, "action": "prevTab" },
|
||||
{ "icon": null, "name": { "key": "OpenSettingsCommandKey" }, "action": "openSettings" },
|
||||
],
|
||||
```
|
||||
|
||||
We'll check at parse time if the `name` property is a string or an object. If
|
||||
it's a string, we'll treat that string as the literal text. Otherwise, if it's
|
||||
an object, we'll attempt to use the `key` property of that object to look up a
|
||||
string from our `ResourceDictionary`. This way, we'll be able to ship localized
|
||||
strings for all the built-in commands, while also allowing the user to easily
|
||||
add their own commands.
|
||||
|
||||
During the spec review process, we considered other options for localization as
|
||||
well. The original proposal included options such as having one `defaults.json`
|
||||
file per-locale, and building the Terminal independently for each locale. Those
|
||||
were not really feasible options, so we instead settled on this solution, as it
|
||||
allowed us to leverage the existing localization support provided to us by the
|
||||
platform.
|
||||
|
||||
The `{ "key": "resourceName" }` solution proposed here was also touched on in
|
||||
[#5280].
|
||||
|
||||
### Proposed Defaults
|
||||
|
||||
These are the following commands I'm proposing adding to the command palette by
|
||||
default. These are largely the actions that are bound by default.
|
||||
|
||||
```json
|
||||
"commands": [
|
||||
{ "icon": null, "name": { "key": "NewTabCommandKey" }, "action": "newTab" },
|
||||
{ "icon": null, "name": { "key": "DuplicateTabCommandKey" }, "action": "duplicateTab" },
|
||||
{ "icon": null, "name": { "key": "DuplicatePaneCommandKey" }, "action": { "action": "splitPane", "split":"auto", "splitMode": "duplicate" } },
|
||||
{ "icon": null, "name": { "key": "SplitHorizontalCommandKey" }, "action": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "icon": null, "name": { "key": "SplitVerticalCommandKey" }, "action": { "action": "splitPane", "split": "vertical" } },
|
||||
|
||||
{ "icon": null, "name": { "key": "CloseWindowCommandKey" }, "action": "closeWindow" },
|
||||
{ "icon": null, "name": { "key": "ClosePaneCommandKey" }, "action": "closePane" },
|
||||
|
||||
{ "icon": null, "name": { "key": "OpenNewTabDropdownCommandKey" }, "action": "openNewTabDropdown" },
|
||||
{ "icon": null, "name": { "key": "OpenSettingsCommandKey" }, "action": "openSettings" },
|
||||
|
||||
{ "icon": null, "name": { "key": "FindCommandKey" }, "action": "find" },
|
||||
|
||||
{ "icon": null, "name": { "key": "NextTabCommandKey" }, "action": "nextTab" },
|
||||
{ "icon": null, "name": { "key": "PrevTabCommandKey" }, "action": "prevTab" },
|
||||
|
||||
{ "icon": null, "name": { "key": "ToggleFullscreenCommandKey" }, "action": "toggleFullscreen" },
|
||||
|
||||
{ "icon": null, "name": { "key": "CopyTextCommandKey" }, "action": { "action": "copy", "singleLine": false } },
|
||||
{ "icon": null, "name": { "key": "PasteCommandKey" }, "action": "paste" },
|
||||
|
||||
{ "icon": null, "name": { "key": "IncreaseFontSizeCommandKey" }, "action": { "action": "adjustFontSize", "delta": 1 } },
|
||||
{ "icon": null, "name": { "key": "DecreaseFontSizeCommandKey" }, "action": { "action": "adjustFontSize", "delta": -1 } },
|
||||
{ "icon": null, "name": { "key": "ResetFontSizeCommandKey" }, "action": "resetFontSize" },
|
||||
|
||||
{ "icon": null, "name": { "key": "ScrollDownCommandKey" }, "action": "scrollDown" },
|
||||
{ "icon": null, "name": { "key": "ScrollDownPageCommandKey" }, "action": "scrollDownPage" },
|
||||
{ "icon": null, "name": { "key": "ScrollUpCommandKey" }, "action": "scrollUp" },
|
||||
{ "icon": null, "name": { "key": "ScrollUpPageCommandKey" }, "action": "scrollUpPage" }
|
||||
]
|
||||
```
|
||||
|
||||
## Future considerations
|
||||
|
||||
* Commands will provide an easy point for allowing an extension to add its
|
||||
actions to the UI, without forcing the user to bind the extension's actions to
|
||||
a keybinding
|
||||
* Also discussed in [#2046] was the potential for adding a command that inputs a
|
||||
certain commandline to be run by the shell. I felt that was out of scope for
|
||||
this spec, so I'm not including it in detail. I believe that would be
|
||||
accomplished by adding a `inputCommand` action, with two args: `commandline`,
|
||||
a string, and `suppressNewline`, an optional bool, defaulted to false. The
|
||||
`inputCommand` action would deliver the given `commandline` as input to the
|
||||
connection, followed by a newline (as to execute the command).
|
||||
`suppressNewline` would prevent the newline from being added. This would work
|
||||
relatively well, so long as you're sitting at a shell prompt. If you were in
|
||||
an application like `vim`, this might be handy for executing a sequence of
|
||||
vim-specific keybindings. Otherwise, you're just going to end up writing a
|
||||
commandline to the buffer of vim. It would be weird, but not unexpected.
|
||||
* Additionally mentioned in [#2046] was the potential for profile-scoped
|
||||
commands. While that's a great idea, I believe it's out of scope for this
|
||||
spec.
|
||||
* Once [#754] lands, we'll need to make sure to include commands for each action
|
||||
manually in the default settings. This will add some overhead that the
|
||||
developer will need to do whenever they add an action. That's unfortunate, but
|
||||
will be largely beneficial to the end user.
|
||||
* We could theoretically also display the keybinding for a certain command in
|
||||
the `ListViewItem` for the command. We'd need some way to correlate a
|
||||
command's action to a keybinding's action. This could be done in a follow-up
|
||||
task.
|
||||
* We might want to alter the fuzzy-search algorithm, to give higher precedence
|
||||
in the results list to commands with more consecutive matching characters.
|
||||
Alternatively we could give more weight to commands where the search matched
|
||||
the initial character of words in the command.
|
||||
- For example: `ot` would give more weight to "**O**pen **T**ab" than
|
||||
"**O**pen Se**t**tings").
|
||||
* We may want to add a button to the New Tab Button's dropdown to "Show Command
|
||||
Palette". I'm hesitant to keep adding new buttons to that UI, but the command
|
||||
palette is otherwise not highly discoverable.
|
||||
- We could add another button to the UI to toggle the visibility of the
|
||||
command palette. This was the idea initially proposed in [#2046].
|
||||
- For both these options, we may want a global setting to hide that button, to
|
||||
keep the UI as minimal as possible.
|
||||
* [#1571] is a request for customizing the "new tab dropdown" menu. When we get
|
||||
to discussing that design, we should consider also enabling users to add
|
||||
commands from their list of commands to that menu as well.
|
||||
- This is included in the spec in [#5888].
|
||||
* I think it would be cool if there was a small timeout as the user was typing
|
||||
in commandline mode before we try to auto-parse their commandline, to check
|
||||
for errors. Might be useful to help sanity check users. We can always parse
|
||||
their `wt` commandlines safely without having to execute them.
|
||||
* It would be cool if the commands the user typed in Commandline Mode could be
|
||||
saved to a history of some sort, so they could easily be re-entered.
|
||||
- It would be especially cool if it could do this across launches.
|
||||
- We don't really have any way of storing transient data like that in the
|
||||
Terminal, so that would need to be figured out first.
|
||||
- Typically the Command Palette is at the top of the view, with the
|
||||
suggestions below it, so navigating through the history would be _backwards_
|
||||
relative to a normal shell.
|
||||
* Perhaps users will want the ability to configure which side of the window the
|
||||
palette appears on?
|
||||
- This might fit in better with [#3327].
|
||||
* [#3753] is a pull request that covers the addition of an "Advanced Tab
|
||||
Switcher". In an application like VsCode, their advanced tab switcher UI is
|
||||
similar to their command palette UI. It might make sense that the user could
|
||||
use the command palette UI to also navigate to active tabs or panes within the
|
||||
terminal, by control name. We've already outlined how the Command Palette
|
||||
could operate in "Action Mode" or "Commandline Mode" - we could also add
|
||||
"Navigate Mode" on `@`, for navigating between tabs or panes.
|
||||
- The tab switcher could probably largely re-use the command palette UI, but
|
||||
maybe hide the input box by default.
|
||||
* We should make sure to add a setting in the future that lets the user opt-in
|
||||
to showing most-recently used commands _first_ in the search order, and
|
||||
possibly even pre-populating the search box with whatever their last entry
|
||||
was.
|
||||
- I'm thinking these are two _separate_ settings.
|
||||
|
||||
### Nested Commands
|
||||
|
||||
Another idea for a future spec is the concept of "nested commands", where a
|
||||
single command has many sub-commands. This would hide the children commands from
|
||||
the entire list of commands, allowing for much more succinct top-level list of
|
||||
commands, and allowing related commands to be grouped together.
|
||||
- For example, I have a text editor plugin that enables rendering markdown to a
|
||||
number of different styles. To use that command in my text editor, first I hit
|
||||
enter on the "Render Markdown..." command, then I select which style I want to
|
||||
render to, in another list of options. This way, I don't need to have three
|
||||
options for "Render Markdown to github", "Render Markdown to gitlab", all in
|
||||
the top-level list.
|
||||
- We probably also want to allow a nested command set to be evaluated at runtime
|
||||
somehow. Like if we had a "Open New Tab..." command that then had a nested
|
||||
menu with the list of profiles.
|
||||
|
||||
The above might be able to be expressed through some JSON like the following:
|
||||
```json
|
||||
"commands": [
|
||||
{
|
||||
"icon": "...",
|
||||
"name": { "key": "NewTabWithProfileRootCommandName" },
|
||||
"commands": [
|
||||
{
|
||||
"iterateOn": "profiles",
|
||||
"icon": "${profile.icon}",
|
||||
"name": { "key": "NewTabWithProfileCommandName" },
|
||||
"command": { "action": "newTab", "profile": "${profile.name}" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"icon": "...",
|
||||
"name": "Connect to ssh...",
|
||||
"commands": [
|
||||
{
|
||||
"icon": "...",
|
||||
"name": "first.com",
|
||||
"command": { "action": "newTab", "commandline": "ssh me@first.com" }
|
||||
},
|
||||
{
|
||||
"icon": "...",
|
||||
"name": "second.com",
|
||||
"command": { "action": "newTab", "commandline": "ssh me@second.com" }
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"icon": "...",
|
||||
"name": { "key": "SplitPaneWithProfileRootCommandName" },
|
||||
"commands": [
|
||||
{
|
||||
"iterateOn": "profiles",
|
||||
"icon": "${profile.icon}",
|
||||
"name": { "key": "SplitPaneWithProfileCommandName" },
|
||||
"commands": [
|
||||
{
|
||||
"icon": "...",
|
||||
"name": { "key": "SplitPaneName" },
|
||||
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "automatic" }
|
||||
},
|
||||
{
|
||||
"icon": "...",
|
||||
"name": { "key": "SplitPaneVerticalName" },
|
||||
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" }
|
||||
},
|
||||
{
|
||||
"icon": "...",
|
||||
"name": { "key": "SplitPaneHorizontalName" },
|
||||
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
This would define three commands, each with a number of nested commands underneath it:
|
||||
* For the first command:
|
||||
- It uses the XAML resource `NewTabWithProfileRootCommandName` as it's name.
|
||||
- Activating this command would cause us to remove all the other commands from
|
||||
the command palette, and only show the nested commands.
|
||||
- It contains nested commands, one for each profile.
|
||||
- Each nested command would use the XAML resource
|
||||
`NewTabWithProfileCommandName`, which then would also contain the string
|
||||
`${profile.name}`, to be filled with the profile's name in the command's
|
||||
name.
|
||||
- It would also use the profile's icon as the command icon.
|
||||
- Activating any of the nested commands would dispatch an action to create a
|
||||
new tab with that profile
|
||||
* The second command:
|
||||
- It uses the string literal `"Connect to ssh..."` as it's name
|
||||
- It contains two nested commands:
|
||||
- Each nested command has it's own literal name
|
||||
- Activating these commands would cause us to open a new tab with the
|
||||
provided `commandline` instead of the default profile's `commandline`
|
||||
* The third command:
|
||||
- It uses the XAML resource `NewTabWithProfileRootCommandName` as it's name.
|
||||
- It contains nested commands, one for each profile.
|
||||
- Each one of these sub-commands each contains 3 subcommands - one that will
|
||||
create a new split pane automatically, one vertically, and one
|
||||
horizontally, each using the given profile.
|
||||
|
||||
So, you could imagine the entire tree as follows:
|
||||
|
||||
```
|
||||
<Command Palette>
|
||||
├─ New Tab With Profile...
|
||||
│ ├─ Profile 1
|
||||
│ ├─ Profile 2
|
||||
│ └─ Profile 3
|
||||
├─ Connect to ssh...
|
||||
│ ├─ first.com
|
||||
│ └─ second.com
|
||||
└─ New Pane...
|
||||
├─ Profile 1...
|
||||
| ├─ Split Automatically
|
||||
| ├─ Split Vertically
|
||||
| └─ Split Horizontally
|
||||
├─ Profile 2...
|
||||
| ├─ Split Automatically
|
||||
| ├─ Split Vertically
|
||||
| └─ Split Horizontally
|
||||
└─ Profile 3...
|
||||
├─ Split Automatically
|
||||
├─ Split Vertically
|
||||
└─ Split Horizontally
|
||||
```
|
||||
|
||||
Note that the palette isn't displayed like a tree - it only ever displays the
|
||||
commands from one single level at a time. So at first, only:
|
||||
|
||||
* New Tab With Profile...
|
||||
* Connect to ssh...
|
||||
* New Pane...
|
||||
|
||||
are visible. Then, when the user <kbd>enter</kbd>'s on one of these (like "New
|
||||
Pane"), the UI will change to display:
|
||||
|
||||
* Profile 1...
|
||||
* Profile 2...
|
||||
* Profile 3...
|
||||
|
||||
### Configuring the Action/Commandline Mode prefix
|
||||
|
||||
As always, I'm also on board with the "this should be configurable by the user"
|
||||
route, so they can change what mode the command palette is in by default, and
|
||||
what the prefixes for different modes are, but I'm not sure how we'd define that
|
||||
cleanly in the settings.
|
||||
|
||||
```json
|
||||
{
|
||||
"commandPaletteActionModePrefix": "", // or null, for no prefix
|
||||
"commandPaletteCommandlineModePrefix": ">"
|
||||
}
|
||||
```
|
||||
|
||||
We'd need to have validation on that though, what if both of them were set to
|
||||
`null`? One of them would _need_ to be `null`, so if both have a character, do
|
||||
we just assume one is the default?
|
||||
|
||||
## Resources
|
||||
Initial post that inspired this spec: #[2046](https://github.com/microsoft/terminal/issues/2046)
|
||||
|
||||
Keybindings args: #[1349](https://github.com/microsoft/terminal/pull/1349)
|
||||
|
||||
Cascading User & Default Settings: #[754](https://github.com/microsoft/terminal/issues/754)
|
||||
|
||||
Untie "active control" from "currently XAML-focused control" #[1205](https://github.com/microsoft/terminal/issues/1205)
|
||||
|
||||
Allow dropdown menu customization in profiles.json [#1571](https://github.com/microsoft/terminal/issues/1571)
|
||||
|
||||
Search or run a command in Dropdown menu [#3879]
|
||||
|
||||
Spec: Introduce a mini-specification for localized resource use from JSON [#5280]
|
||||
|
||||
<!-- Footnotes -->
|
||||
[#754]: https://github.com/microsoft/terminal/issues/754
|
||||
[#1205]: https://github.com/microsoft/terminal/issues/1205
|
||||
[#1142]: https://github.com/microsoft/terminal/pull/1349
|
||||
[#2046]: https://github.com/microsoft/terminal/issues/2046
|
||||
[#1571]: https://github.com/microsoft/terminal/issues/1571
|
||||
[#3879]: https://github.com/microsoft/terminal/issues/3879
|
||||
[#5280]: https://github.com/microsoft/terminal/pull/5280
|
||||
[#4472]: https://github.com/microsoft/terminal/issues/4472
|
||||
[#3327]: https://github.com/microsoft/terminal/issues/3327
|
||||
[#3753]: https://github.com/microsoft/terminal/pulls/3753
|
||||
[#5888]: https://github.com/microsoft/terminal/pulls/5888
|
||||
135
doc/specs/#2557 - Settings Keybinding.md
Normal file
135
doc/specs/#2557 - Settings Keybinding.md
Normal file
@@ -0,0 +1,135 @@
|
||||
---
|
||||
author: Carlos Zamora @carlos-zamora
|
||||
created on: 2020-05-14
|
||||
last updated: 2020-05-14
|
||||
issue id: #2557
|
||||
---
|
||||
|
||||
# Open Settings Keybinding
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec outlines an expansion to the existing `openSettings` keybinding.
|
||||
|
||||
## Inspiration
|
||||
|
||||
As a Settings UI becomes more of a reality, the behavior of this keybinding will be expanded on to better interact with the UI. Prior to a Settings UI, there was only one concept of the modifiable user settings: settings.json.
|
||||
|
||||
Once the Settings UI is created, we can expect users to want to access the following scenarios:
|
||||
- Settings UI: globals page
|
||||
- Settings UI: profiles page
|
||||
- Settings UI: color schemes page
|
||||
- Settings UI: keybindings page
|
||||
- settings.json
|
||||
- defaults.json
|
||||
These are provided as non-comprehensive examples of pages that might be in a future Settings UI. The rest of the doc assumes these are the pages in the Settings UI.
|
||||
|
||||
|
||||
## Solution Design
|
||||
Originally, #2557 was intended to allow for a keybinding arg to access defaults.json. I imagined a keybinding arg such as "openDefaults: true/false" to accomplish this. However, this is not expandable in the following scenarios:
|
||||
- what if we decide to create more settings files in the future? (i.e. themes.json, extensions.json, etc...)
|
||||
- when the Settings UI comes in, there is ambiguity as to what `openSettings` does (json? UI? Which page?)
|
||||
|
||||
### Proposition 1.1: the minimal `target` arg
|
||||
Instead, what if we introduced a new `target` keybinding argument, that could be used as follows:
|
||||
| Keybinding Command | Behavior |
|
||||
|--|--|
|
||||
| `"command": { "action": "openSettings", "target": "settingsFile" }` | opens "settings.json" in your default text editor |
|
||||
| `"command": { "action": "openSettings", "target": "defaultsFile" }` | opens "defaults.json" in your default text editor |
|
||||
| `"command": { "action": "openSettings", "target": "allSettingsFiles" }` | opens all of settings files in your default text editor |
|
||||
| `"command": { "action": "openSettings", "target": "settingsUI" }` | opens the Settings UI |
|
||||
|
||||
This was based on Proposition 1 below, but reduced the overhead of people able to define specific pages to go to.
|
||||
|
||||
### Other options we considered were...
|
||||
|
||||
#### Proposition 1: the `target` arg
|
||||
We considered making target be more specific like this:
|
||||
| Keybinding Command | Behavior |
|
||||
|--|--|
|
||||
| `"command": { "action": "openSettings", "target": "settingsFile" }` | opens "settings.json" in your default text editor |
|
||||
| `"command": { "action": "openSettings", "target": "defaultsFile" }` | opens "defaults.json" in your default text editor |
|
||||
| `"command": { "action": "openSettings", "target": "uiSettings" }` | opens the Settings UI |
|
||||
| `"command": { "action": "openSettings", "target": "uiGlobals" }` | opens the Settings UI to the Globals page |
|
||||
| `"command": { "action": "openSettings", "target": "uiProfiles" }` | opens the Settings UI to the Profiles page |
|
||||
| `"command": { "action": "openSettings", "target": "uiColorSchemes" }` | opens the Settings UI to the Color Schemes page |
|
||||
|
||||
If the Settings UI does not have a home page, `uiGlobals` and `uiSettings` will do the same thing.
|
||||
|
||||
This provides the user with more flexibility to decide what settings page to open and how to access it.
|
||||
|
||||
#### Proposition 2: the `format` and `page` args
|
||||
Another approach would be to break up `target` into `format` and `page`.
|
||||
|
||||
`format` would be either `json` or `ui`, dictating how you can access the setting.
|
||||
`page` would be any of the categories we have for settings: `settings`, `defaults`, `globals`, `profiles`, etc...
|
||||
|
||||
This could look like this:
|
||||
| Keybinding Command | Behavior |
|
||||
|--|--|
|
||||
| `"command": { "action": "openSettings", "format": "json", "page": "settings" }` | opens "settings.json" in your default text editor |
|
||||
| `"command": { "action": "openSettings", "format": "json", "page": "defaults" }` | opens "defaults.json" in your default text editor |
|
||||
| `"command": { "action": "openSettings", "format": "ui", "page": "settings" }` | opens the Settings UI |
|
||||
| `"command": { "action": "openSettings", "format": "ui", "page": "globals" }` | opens the Settings UI to the Globals page |
|
||||
| `"command": { "action": "openSettings", "format": "ui", "page": "profiles" }` | opens the Settings UI to the Profiles page |
|
||||
| `"command": { "action": "openSettings", "format": "ui", "page": "colorSchemes" }` | opens the Settings UI to the Color Schemes page |
|
||||
|
||||
The tricky thing for this approach is, what do we do in the following scenario:
|
||||
```js
|
||||
{ "command": { "action": "openSettings", "format": "json", "page": "colorSchemes" } }
|
||||
```
|
||||
In situations like this, where the user wants a `json` format, but chooses a `page` that is a part of a larger settings file, I propose we simply open `settings.json` (or whichever file contains the settings for the desired feature).
|
||||
|
||||
#### Proposition 3: minimal approach
|
||||
What if we don't need to care about the page, and we really just cared about the format: UI vs json? Then, we still need a way to represent opening defaults.json. We could simplify Proposition 2 to be as follows:
|
||||
- `format`: `json`, `ui`
|
||||
- ~`page`~ `openDefaults`: `true`, `false`
|
||||
|
||||
Here, we take away the ability to specifically choose which page the user wants to open, but the result looks much cleaner.
|
||||
|
||||
If there are concerns about adding more settings files in the future, `openDefaults` could be renamed to be `target`, and this would still serve as a hybrid of Proposition 1 and 2, with less possible options.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
The user has full control over modifying and adding these keybindings.
|
||||
|
||||
However, the question arises for what the default experience should be. I propose the following:
|
||||
| Keychord | Behavior |
|
||||
| <kbd>ctrl+,</kbd> | Open settings.json |
|
||||
| <kbd>ctrl+alt+,</kbd> | Open defaults.json |
|
||||
|
||||
When the Settings UI gets added in, they will be updated to open their respective pages in the Settings UI.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
None.
|
||||
|
||||
### Security
|
||||
|
||||
None.
|
||||
|
||||
### Reliability
|
||||
|
||||
None.
|
||||
|
||||
### Compatibility
|
||||
|
||||
Users that expect a json file to open would have to update their keybinding to do so.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
## Potential Issues
|
||||
|
||||
None.
|
||||
|
||||
## Future considerations
|
||||
|
||||
When the Settings UI becomes available, a new value for `target` of `settingsUI` will be added and it will become the default target.
|
||||
|
||||
If the community finds value in opening to a specific page of the Settings UI, `target` will be responsible for providing that functionality.
|
||||
|
||||
## Resources
|
||||
|
||||
None.
|
||||
88
doc/specs/#4191 - Formatted Copy/spec.md
Normal file
88
doc/specs/#4191 - Formatted Copy/spec.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
author: Kayla Cinnamon @cinnamon-msft
|
||||
created on: 2020-04-01
|
||||
last updated: 2020-04-07
|
||||
issue id: #4191
|
||||
---
|
||||
|
||||
# Formatted Copy
|
||||
|
||||
## Abstract
|
||||
|
||||
When copying text, the Terminal should provide the option of including formatting. Not all apps that receive text allow for picking which format you want when pasting. The default should be to only copy plain text, based on the response from this poll on Twitter.
|
||||
|
||||

|
||||
|
||||
## Solution Proposals
|
||||
|
||||
A proposal for the right click behavior as well as two user settings proposals are described below. The conclusion the team arrived at is at the bottom under the [Conclusions section](#conclusions).
|
||||
|
||||
1. [Settings option 1 - global setting](#settings-option-1---global-setting)
|
||||
2. [Settings option 2 - key binding argument](#settings-option-2---key-binding-argument)
|
||||
3. [Right click behavior](#right-click-behavior)
|
||||
|
||||
### Settings option 1 - global setting
|
||||
|
||||
We could have a global setting that when enabled, would copy formatting to the clipboard on all copy operations.
|
||||
|
||||
### Settings option 2 - key binding argument
|
||||
|
||||
We could add an argument to the `copy` key binding argument to allow for formatted copying when the user chooses to do so.
|
||||
|
||||
### Right click behavior
|
||||
|
||||
By default, right clicking to copy would only copy the plain text.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
### Settings option 1 - global setting
|
||||
|
||||
a. The user could list which kinds of formats they want included when they copy. When right clicking, they would copy with these formats.
|
||||
|
||||
`"copyFormats": ["html","rtf","plain"]`
|
||||
|
||||
b. We could also just combine html and rtf into a single boolean. Users would either get plain text only (`false`) or all formatting (`true`) onto their clipboard. If this is set to `true`, the default right click behavior is reversed: right clicking copies the formatting.
|
||||
|
||||
`"copyFormatting": true`
|
||||
|
||||
### Settings option 2 - key binding argument
|
||||
|
||||
a. Just like the `trimWhitespace` argument you can add to the `copy` key binding, we could add one for text formatting. This would not change the right click behavior.
|
||||
|
||||
`{"command": {"action": "copy", "keepFormatting": true}, "keys": "ctrl+a"}`
|
||||
|
||||
b. We could also split out the html and rtf formats. The right click behavior would still stay as default.
|
||||
|
||||
`{"command": {"action": "copy", "formats": ["html","rtf","plain"]}, "keys": "ctrl+a"}`
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
This shouldn't affect accessibility.
|
||||
|
||||
### Security
|
||||
|
||||
This does not affect security.
|
||||
|
||||
### Reliability
|
||||
|
||||
This does not affect reliability.
|
||||
|
||||
### Compatibility
|
||||
|
||||
This breaks the existing behavior of always copying the formatting. The justification for breaking this default behavior is in response to the community saying the default should be plain text only.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
## Potential Issues
|
||||
|
||||
One possible issue is that discovering how to copy the formatting might be difficult to find. We could mitigate this by adding it into the settings.json file and commenting it out.
|
||||
|
||||
## Conclusions
|
||||
|
||||
The team has decided to have plain text as the default copy behavior and to enable formatted copying with a global setting that accepts a boolean value (settings option 1 - global setting, option b). In the future, we can modify this setting to also accept an array, so the user can specify which formats they would like to copy. Additionally, a key binding can be added to allow for greater flexibility.
|
||||
|
||||
## Future considerations
|
||||
|
||||
We could always add an additional option if people want more flexibility. For example, if we ship a global setting now, we could ship a key binding later that lets you choose how you want to copy, and vice versa. Additionally, we can add functionality to the global setting that allows for specific formats or styles to be copied.
|
||||
BIN
doc/specs/#4191 - Formatted Copy/twitter-poll.png
Normal file
BIN
doc/specs/#4191 - Formatted Copy/twitter-poll.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
559
doc/specs/#4999 - Improved keyboard handling in Conpty.md
Normal file
559
doc/specs/#4999 - Improved keyboard handling in Conpty.md
Normal file
@@ -0,0 +1,559 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2020-05-07
|
||||
last updated: 2020-06-03
|
||||
issue id: 4999
|
||||
---
|
||||
|
||||
# Improved keyboard handling in Conpty
|
||||
|
||||
## Abstract
|
||||
|
||||
The Windows Console internally uses [`INPUT_RECORD`]s to represent the various
|
||||
types of input that a user might send to a client application. This includes
|
||||
things like keypresses, mouse events, window resizes, etc.
|
||||
|
||||
However, conpty's keyboard input is fundamentally backed by VT sequences, which
|
||||
limits the range of keys that a terminal application can actually send relative
|
||||
to what the console was capable of. This results in a number of keys that were
|
||||
previously representable in the console as `INPUT_RECORD`s, but are impossible
|
||||
to send to a client application that's running in conpty mode.
|
||||
|
||||
Some of these issues include, but are not limited to:
|
||||
|
||||
* Some keybindings used by PSReadLine aren't getting through [#879]
|
||||
* Bug Report: Control+Space not sent to terminal emulator. [#2865]
|
||||
* Shift+Enter always submits, breaking PSReadline features [#530]
|
||||
* Powershell: Ctrl-Alt-? does not work in Windows Terminal [#3079]
|
||||
* Bug: ctrl+break is not ctrl+c [#1119]
|
||||
* Something wrong with keyboard modifiers processing? [#1694]
|
||||
* Numeric input not accepted by choice.exe [#3608]
|
||||
* Ctrl+Keys that can't be encoded as VT should still fall through as the unmodified character [#3483]
|
||||
* Modifier keys are not properly propagated to application hosted in Windows Terminal [#4334] / [#4446]
|
||||
|
||||
This spec covers a mechanism by which we can add support to ConPTY so that a
|
||||
terminal application could send `INPUT_RECORD`-like key events to conpty,
|
||||
enabling client applications to receive the full range of keys once again.
|
||||
Included at the bottom of this document is a collection of [options that were
|
||||
investigated](#options-considered) as a part of preparing this document.
|
||||
|
||||
## Considerations
|
||||
|
||||
When evaluating existing encoding schemes for viability, the following things
|
||||
were used to evaluate whether or not a particular encoding would work for our
|
||||
needs:
|
||||
|
||||
* Would a particular encoding be mixable with other normal VT processing easily?
|
||||
- How would the Terminal know when it should send a \<chosen_encoding> key vs
|
||||
a normally encoded one?
|
||||
- For ex, <kbd>Ctrl+space</kbd> - should we send `NUL` or
|
||||
\<chosen_encoding's version of ctrl+space>
|
||||
* If there's a scenario where Windows Terminal might _not_ be connected to a
|
||||
conpty, then how does conpty enable \<chosen_encoding>?
|
||||
* Is the goal "Full `INPUT_RECORD` fidelity" or "Make the above scenarios work"?
|
||||
- One could imagine having the Terminal special-case the above keys, and send
|
||||
the xterm modifyOtherKeys sequences just for those scenarios.
|
||||
- This would _not_ work for <kbd>shift</kbd> all by itself.
|
||||
- In my _opinion_, "just making the above work" is a subset of "full
|
||||
INPUT_RECORD", and inevitably we're going to want "full INPUT_RECORD"
|
||||
|
||||
The goal we're trying to achieve is communicating `INPUT_RECORD`s from the
|
||||
terminal to the client app via conpty. This isn't supposed to be a \*nix
|
||||
terminal compatible communication, it's supposed to be fundamentally Win32-like.
|
||||
|
||||
Keys that we definitely need to support, that don't have unique VT sequences:
|
||||
* <kbd>Ctrl+Space</kbd> ([#879], [#2865])
|
||||
* <kbd>Shift+Enter</kbd> ([#530])
|
||||
* <kbd>Ctrl+Break</kbd> ([#1119])
|
||||
* <kbd>Ctrl+Alt+?</kbd> ([#3079])
|
||||
* <kbd>Ctrl</kbd>, <kbd>Alt</kbd>, <kbd>Shift</kbd>, (without another keydown/up) ([#3608], [#4334], [#4446])
|
||||
|
||||
> 👉 NOTE: There are actually 5 types of events that can all be encoded as an
|
||||
> `INPUT_RECORD`. This spec primarily focuses on the encoding of
|
||||
> `KEY_EVENT_RECORD`s. It is left as a Future Consideration to add support for
|
||||
> the other types of `INPUT_RECORD` as other sequences, which could be done
|
||||
> trivially similarly to the following proposal.
|
||||
|
||||
## Solution Design
|
||||
|
||||
### Inspiration
|
||||
|
||||
The design we've settled upon is one that's highly inspired by a few precedents:
|
||||
* `Application Cursor Keys (DECCKM)` is a long-supported VT sequence which a
|
||||
client application can use to request a different input format from the
|
||||
Terminal. This is the DECSET sequence `^[[?1h`/`^[[?1l` (for enable/disable,
|
||||
respectively). This changes the sequences sent by keys like the Arrow keys
|
||||
from a sequence like `^[[A` to `^[OA` instead.
|
||||
* The `kitty` terminal emulator uses a similar DECSET sequence for enabling
|
||||
their own input format, which they call ["full mode"]. Similar to DECCKM, this
|
||||
changes the format of the sequences that the terminal should send for keyboard
|
||||
input. Their "full mode" contains much more information when keys are pressed
|
||||
or released (though, less than a full `INPUT_RECORD` worth of data). Instead
|
||||
of input being sent to the client as a CSI or SS3 sequence, this `kitty` mode
|
||||
uses "Application Program-Command" (or "APC") sequences , prefixed with `^[_`.
|
||||
* [iTerm2](https://www.iterm2.com/documentation-escape-codes.html) has a region
|
||||
of OSC's that they've carved for themselves all starting with the same initial
|
||||
parameter, `1337`. They then have a number of commands that all use the second
|
||||
parameter to indicate what command specific to iTerm2 they're actually
|
||||
implementing.
|
||||
|
||||
### Requesting `win32-input-mode`
|
||||
|
||||
An application can request `win32-input-mode` with the following private mode sequence:
|
||||
|
||||
```
|
||||
^[ [ ? 9001 h/l
|
||||
l: Disable win32-input-mode
|
||||
h: Enable win32-input-mode
|
||||
```
|
||||
|
||||
Private mode `9001` seems unused according to the [xterm
|
||||
documentation](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html). This is
|
||||
stylistically similar to how `DECKPM`, `DECCKM`, and `kitty`'s ["full mode"] are
|
||||
enabled.
|
||||
|
||||
> 👉 NOTE: an earlier draft of this spec used an OSC sequence for enabling these
|
||||
> sequences. This was abandoned in favor of the more stylistically consistent
|
||||
> private mode params proposed above. Additionally, if implemented as a private
|
||||
> mode, then a client app could query if this setting was set with `DECRQM`
|
||||
|
||||
When a terminal receives a `^[[?9001h` sequence, they should switch into
|
||||
`win32-input-mode`. In `win32-input-mode`, the terminal will send keyboard input
|
||||
to the connected client application in the following format:
|
||||
|
||||
### `win32-input-mode` sequences
|
||||
|
||||
The `KEY_EVENT_RECORD` portion of an input record (the part that's important for
|
||||
us to encode in this feature) is defined as the following:
|
||||
|
||||
```c++
|
||||
typedef struct _KEY_EVENT_RECORD {
|
||||
BOOL bKeyDown;
|
||||
WORD wRepeatCount;
|
||||
WORD wVirtualKeyCode;
|
||||
WORD wVirtualScanCode;
|
||||
union {
|
||||
WCHAR UnicodeChar;
|
||||
CHAR AsciiChar;
|
||||
} uChar;
|
||||
DWORD dwControlKeyState;
|
||||
} KEY_EVENT_RECORD;
|
||||
```
|
||||
|
||||
To encode all of this information, I propose the following sequence. This is a
|
||||
CSI sequence with a final terminator character of `_`. This character appears to
|
||||
only be used as a terminator for the [SCO input
|
||||
sequence](https://vt100.net/docs/vt510-rm/chapter6.html) for
|
||||
<kbd>Ctrl+Shift+F10</kbd>. This conflict isn't a real concern for us
|
||||
compatibility wise. For more details, see [SCO
|
||||
Compatibility](#SCO-compatibility) below.
|
||||
|
||||
```
|
||||
^[ [ Vk ; Sc ; Uc ; Kd ; Cs ; Rc _
|
||||
|
||||
Vk: the value of wVirtualKeyCode - any number. If omitted, defaults to '0'.
|
||||
|
||||
Sc: the value of wVirtualScanCode - any number. If omitted, defaults to '0'.
|
||||
|
||||
Uc: the decimal value of UnicodeChar - for example, NUL is "0", LF is
|
||||
"10", the character 'A' is "65". If omitted, defaults to '0'.
|
||||
|
||||
Kd: the value of bKeyDown - either a '0' or '1'. If omitted, defaults to '0'.
|
||||
|
||||
Cs: the value of dwControlKeyState - any number. If omitted, defaults to '0'.
|
||||
|
||||
Rc: the value of wRepeatCount - any number. If omitted, defaults to '1'.
|
||||
|
||||
```
|
||||
|
||||
> 👉 NOTE: an earlier draft of this spec used an APC sequence for encoding the
|
||||
> input sequences. This was changed to a CSI for stylistic reasons. There's not
|
||||
> a great body of reference anywhere that lists APC sequences in use, so there's
|
||||
> no way to know if the sequence would collide with another terminal emulator's
|
||||
> usage. Furthermore, using an APC seems to give a distinct impression that
|
||||
> this is some "Windows Terminal" specific sequence, which is not intended. This
|
||||
> is a Windows-specific sequence, but one that any Terminal/application could
|
||||
> use.
|
||||
|
||||
In this way, a terminal can communicate input to a connected client application
|
||||
as `INPUT_RECORD`s, without any loss of fidelity.
|
||||
|
||||
#### Example
|
||||
|
||||
When the user presses <kbd>Ctrl+F1</kbd> in the console, the console actually
|
||||
send 4 input records to the client application:
|
||||
* A <kbd>Ctrl</kbd> down event
|
||||
* A <kbd>F1</kbd> down event
|
||||
* A <kbd>F1</kbd> up event
|
||||
* A <kbd>Ctrl</kbd> up event
|
||||
|
||||
Encoded in `win32-input-mode`, this would look like the following:
|
||||
```
|
||||
^[[17;29;0;1;8;1_
|
||||
^[[112;59;0;1;8;1_
|
||||
^[[112;59;0;0;8;1_
|
||||
^[[17;29;0;0;0;1_
|
||||
|
||||
Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x28
|
||||
Down: 1 Repeat: 1 KeyCode: 0x70 ScanCode: 0x3b Char: \0 (0x0) KeyState: 0x28
|
||||
Down: 0 Repeat: 1 KeyCode: 0x70 ScanCode: 0x3b Char: \0 (0x0) KeyState: 0x28
|
||||
Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x20
|
||||
```
|
||||
|
||||
Similarly, for a keypress like <kbd>Ctrl+Alt+A</kbd>, which is 6 key events:
|
||||
```
|
||||
^[[17;29;0;1;8;1_
|
||||
^[[18;56;0;1;10;1_
|
||||
^[[65;30;0;1;10;1_
|
||||
^[[65;30;0;0;10;1_
|
||||
^[[18;56;0;0;8;1_
|
||||
^[[17;29;0;0;0;1_
|
||||
|
||||
Down: 1 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x28
|
||||
Down: 1 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x2a
|
||||
Down: 1 Repeat: 1 KeyCode: 0x41 ScanCode: 0x1e Char: \0 (0x0) KeyState: 0x2a
|
||||
Down: 0 Repeat: 1 KeyCode: 0x41 ScanCode: 0x1e Char: \0 (0x0) KeyState: 0x2a
|
||||
Down: 0 Repeat: 1 KeyCode: 0x12 ScanCode: 0x38 Char: \0 (0x0) KeyState: 0x28
|
||||
Down: 0 Repeat: 1 KeyCode: 0x11 ScanCode: 0x1d Char: \0 (0x0) KeyState: 0x20
|
||||
```
|
||||
|
||||
Or, for something simple like <kbd>A</kbd> (which is 4 key events):
|
||||
```
|
||||
^[[16;42;0;1;16;1_
|
||||
^[[65;30;65;1;16;1_
|
||||
^[[16;42;0;0;0;1_
|
||||
^[[65;30;97;0;0;1_
|
||||
|
||||
Down: 1 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x30
|
||||
Down: 1 Repeat: 1 KeyCode: 0x41 ScanCode: 0x1e Char: A (0x41) KeyState: 0x30
|
||||
Down: 0 Repeat: 1 KeyCode: 0x10 ScanCode: 0x2a Char: \0 (0x0) KeyState: 0x20
|
||||
Down: 0 Repeat: 1 KeyCode: 0x41 ScanCode: 0x1e Char: a (0x61) KeyState: 0x20
|
||||
```
|
||||
|
||||
> 👉 NOTE: In all the above examples, I had my NumLock key off. If I had the
|
||||
> NumLock key instead pressed, all the KeyState parameters would have bits 0x20
|
||||
> set. To get these keys with a NumLock, add 32 to the value.
|
||||
|
||||
These parameters are ordered based on how likely they are to be used. Most of
|
||||
the time, the repeat count is not needed (it's almost always `1`), so it can be
|
||||
left off when not required. Similarly, the control key state is probably going
|
||||
to be 0 a lot of the time too, so that is second last. Even keydown will be 0 at
|
||||
least half the time, so that can be omitted some of the time.
|
||||
|
||||
Furthermore, considering omitted values in CSI parameters default to the values
|
||||
specified above, the above sequences could each be shortened to the following.
|
||||
|
||||
* <kbd>Ctrl+F1</kbd>
|
||||
```
|
||||
^[[17;29;;1;8_
|
||||
^[[112;59;;1;8_
|
||||
^[[112;59;;;8_
|
||||
^[[17;29_
|
||||
```
|
||||
|
||||
* <kbd>Ctrl+Alt+A</kbd>
|
||||
```
|
||||
^[[17;29;;1;8_
|
||||
^[[18;56;;1;10_
|
||||
^[[65;30;;1;10_
|
||||
^[[65;30;;;10_
|
||||
^[[18;56;;;8_
|
||||
^[[17;29;;_
|
||||
```
|
||||
|
||||
* <kbd>A</kbd> (which is <kbd>shift+a</kbd>)
|
||||
```
|
||||
^[[16;42;;1;16_
|
||||
^[[65;30;65;1;16_
|
||||
^[[16;42_
|
||||
^[[65;30;97_
|
||||
```
|
||||
|
||||
* Or even easier, just <kbd>a</kbd>
|
||||
```
|
||||
^[[65;30;97;1_
|
||||
^[[65;30;97_
|
||||
```
|
||||
|
||||
### Scenarios
|
||||
|
||||
#### User is typing into WSL from the Windows Terminal
|
||||
|
||||
`WT -> conpty[1] -> wsl`
|
||||
|
||||
* Conpty[1] will ask for `win32-input-mode` from the Windows Terminal when
|
||||
conpty[1] first boots up. Conpty will _always_ ask for win32-input-mode -
|
||||
Terminals that _don't_ support this mode will ignore this sequence on startup.
|
||||
* When the user types keys in Windows Terminal, WT will translate them into
|
||||
win32 sequences and send them to conpty[1]
|
||||
* Conpty[1] will translate those win32 sequences into `INPUT_RECORD`s.
|
||||
- When those `INPUT_RECORD`s are written to the input buffer, they'll be
|
||||
converted into VT sequences corresponding to whatever input mode the linux
|
||||
app is in.
|
||||
* When WSL reads the input, it'll read (using `ReadConsoleInput`) a stream of
|
||||
`INPUT_RECORD`s that contain only character information, which it will then
|
||||
pass to the linux application.
|
||||
- This is how `wsl.exe` behaves today, before this change.
|
||||
|
||||
#### User is typing into `cmd.exe` running in WSL interop
|
||||
|
||||
`WT -> conpty[1] -> wsl -> conpty[2] -> cmd.exe`
|
||||
|
||||
(presuming you start from the previous scenario, and launch `cmd.exe` inside wsl)
|
||||
|
||||
* Conpty[2] will ask for `win32-input-mode` from conpty[1] when conpty[2] first
|
||||
boots up.
|
||||
- As conpty[1] is just a conhost that knows how to handle
|
||||
`win32-input-mode`, it will switch its own VT input handling into
|
||||
`win32-input-mode`
|
||||
* When the user types keys in Windows Terminal, WT will translate them into
|
||||
win32 sequences and send them to conpty[1]
|
||||
* Conpty[1] will translate those win32 sequences into `INPUT_RECORD`s. When
|
||||
conpty[1] writes these to its buffer, it will translate the `INPUT_RECORD`s
|
||||
into VT sequences for the `win32-input-mode`. This is because it believes the
|
||||
client (in this case, the conpty[2] running attached to `wsl`) wants
|
||||
`win32-input-mode`.
|
||||
* When WSL reads the input, it'll read (using `ReadConsoleInput`) a stream of
|
||||
`INPUT_RECORD`s that contain only character information, which it will then
|
||||
use to pass a stream of characters to conpty[2].
|
||||
* Conpty[2] will get those sequences, and will translate those win32 sequences
|
||||
into `INPUT_RECORD`s
|
||||
* When `cmd.exe` reads the input, they'll receive the full `INPUT_RECORD`s
|
||||
they're expecting
|
||||
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
This is not a user-facing feature.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
### Security
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
### Reliability
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
### Compatibility
|
||||
|
||||
This isn't expected to break any existing scenarios. The information that we're
|
||||
passing to conpty from the Terminal should strictly have _more_ information in
|
||||
them than they used to. Conhost was already capable of translating
|
||||
`INPUT_RECORD`s back into VT sequences, so this should work the same as before.
|
||||
|
||||
There's some hypothetical future where the Terminal isn't connected to conpty.
|
||||
In that future, the Terminal will still be able to work correctly, even with
|
||||
this ConPTY change. The Terminal will only switch into sending
|
||||
`win32-input-mode` sequences when _conpty asks for them_. Otherwise, the
|
||||
Terminal will still behave like a normal terminal emulator.
|
||||
|
||||
#### Terminals that don't support `?9001h`
|
||||
|
||||
Traditionally, whenever a terminal emulator doesn't understand a particular VT
|
||||
sequence, they simply ignore the unknown sequence. This assumption is being
|
||||
relied upon heavily, as ConPTY will _always_ emit a `^[[?9001h` on
|
||||
initialization, to request `win32-input-mode`.
|
||||
|
||||
#### SCO Compatibility
|
||||
|
||||
As mentioned above, the `_` character is used as a terminator for the [SCO input
|
||||
sequence](https://vt100.net/docs/vt510-rm/chapter6.html) for
|
||||
<kbd>Ctrl+Shift+F10</kbd>. This conflict would be a problem if a hypothetical
|
||||
terminal was connected to conpty that sent input to conpty in SCO format.
|
||||
However, if that terminal was only sending input to conpty in SCO mode, it would
|
||||
have much worse problems than just <kbd>Ctrl+Shift+F10</kbd> not working. If we
|
||||
did want to support SCO mode in the future, I'd even go so far as to say we
|
||||
could maybe treat a `win32-input-mode` sequence with no params as
|
||||
<kbd>Ctrl+Shift+F10</kbd>, considering that `KEY_EVENT_RECORD{0}` isn't really
|
||||
valid anyways.
|
||||
|
||||
#### Remoting `INPUT_RECORD`s
|
||||
|
||||
A potential area of concern is the fact that VT sequences are often used to
|
||||
remote input from one machine to another. For example, a terminal might be
|
||||
running on machine A, and the conpty at the end of the pipe (which is running
|
||||
the client application) might be running on another machine B.
|
||||
|
||||
If these two machines have different keyboard layouts, then it's possible that
|
||||
the `INPUT_RECORD`s synthesized by the terminal on machine A won't really be
|
||||
valid on machine B. It's possible that machine B has a different mapping of scan
|
||||
codes \<-> characters. A client that's running on machine B that uses win32 APIs
|
||||
to try and infer the vkey, scancode, or character from the other information in
|
||||
the `INPUT_RECORD` might end up synthesizing the wrong values.
|
||||
|
||||
At the time of writing, we're not really sure what a good solution to this
|
||||
problem would be. Client applications that use `win32-input-mode` should be
|
||||
aware of this, and be written with the understanding that these values are
|
||||
coming from the terminal's machine, which might not necessarily be the local
|
||||
machine.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
## Potential Issues
|
||||
|
||||
_(no change expected)_
|
||||
|
||||
## Future considerations
|
||||
|
||||
* We could also hypothetically use this same mechanism to send Win32-like mouse
|
||||
events to conpty, since similar to VT keyboard events, VT mouse events don't
|
||||
have the same fidelity that Win32 mouse events do.
|
||||
- We could enable this with a different terminating character, to identify
|
||||
which type of `INPUT_RECORD` event we're encoding.
|
||||
* Client applications that want to be able to read full Win32 keyboard input
|
||||
from `conhost` _using VT_ will also be able to use `^[[?9001h` to do this. If
|
||||
they emit `^[[?9001h`, then conhost will switch itself into
|
||||
`win32-input-mode`, and the client will read `win32-input-mode` encoded
|
||||
sequences as input. This could enable other cross-platform applications to
|
||||
also use win32-like input in the future.
|
||||
|
||||
## Options Considered
|
||||
|
||||
_disclaimer: these notes are verbatim from my research notes in [#4999]_.
|
||||
|
||||
### Create our own format for `INPUT_RECORD`s
|
||||
|
||||
* If we wanted to do this, then we'd probably want to have the Terminal only
|
||||
send input as this format, and not use the existing translator to synthesize
|
||||
VT sequences
|
||||
- Consider sending a ctrl down, '^A', ctrl up. We wouldn't want to send this
|
||||
as three sequences, because conpty will take the '^A' and synthesize
|
||||
_another_ ctrl down, ctrl up pair.
|
||||
* With conpty passthrough mode, we'd still need the `InputStateMachineEngine`
|
||||
to convert these sequences into INPUT_RECORDs to translate back to VT
|
||||
* Wouldn't really expect client apps to ever _need_ this format, but it could
|
||||
always be possible for them to need it in the future.
|
||||
|
||||
#### Pros:
|
||||
* Definitely gets us all the information that we need.
|
||||
* Can handle solo modifiers
|
||||
* Can handle keydown and keyup separately
|
||||
* We can make the sequence however we want to parse it.
|
||||
|
||||
#### Cons:
|
||||
* No reference implementation, so we'd be flying blind
|
||||
* We'd be defining our own VT sequences for these, which we've never done
|
||||
before. This was _inevitable_, however, this is still the first time we'd be
|
||||
doing this.
|
||||
* By having the Terminal send all input as _this protocol_, VT Input passthrough
|
||||
to apps that want VT input won't work anymore for the Terminal. That's _okay_
|
||||
|
||||
### kitty extension
|
||||
[Reference](https://sw.kovidgoyal.net/kitty/protocol-extensions.html#keyboard-handling)
|
||||
|
||||
#### Pros:
|
||||
* Not terribly difficult to decode
|
||||
* Unique from anything else we'd be processing, as it's an APC sequence
|
||||
(`\x1b_`)
|
||||
* From their docs:
|
||||
> All printable key presses without modifier keys are sent
|
||||
just as in the normal mode. ... For non printable keys and key combinations
|
||||
including one or more modifiers, an escape sequence encoding the key event is
|
||||
sent
|
||||
- I think I like this. ASCII and other keyboard layout chars (things that would
|
||||
hit `SendChar`) would still just come through as the normal char.
|
||||
|
||||
#### Cons:
|
||||
* Their encoding table is _odd_. [Look at
|
||||
this](https://sw.kovidgoyal.net/kitty/key-encoding.html). What order is that
|
||||
in? Obviously the first column is sorted alphabetically, but the mapping of
|
||||
key->char is in a certainly hard to decipher order.
|
||||
* I can't get it working locally, so hard to test 😐
|
||||
* They do declare the `fullkbd` terminfo capability to identify that they
|
||||
support this mode, but I'm not sure anyone else uses it.
|
||||
- I'm also not sure that any _client_ apps are reading this currently.
|
||||
* This isn't designed to be full `KEY_EVENT`s - where would we put the scancode
|
||||
(for apps that think that's important)?
|
||||
- We'd have to extend this protocol _anyways_
|
||||
|
||||
### `xterm` "Set key modifier options"
|
||||
Notably looking at
|
||||
[`modifyOtherKeys`](https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys).
|
||||
|
||||
#### Pros:
|
||||
* `xterm` implements this so there's a reference implementation
|
||||
* relatively easy to parse these sequences. `CSI 27 ; <modifiers> ; <key> ~`
|
||||
|
||||
#### Cons:
|
||||
* Only sends the sequence on key-up
|
||||
* Doesn't send modifiers all on their own
|
||||
|
||||
### `DECPCTERM`
|
||||
[VT100.net doc](https://vt100.net/docs/vt510-rm/DECPCTERM.html)
|
||||
|
||||
#### Pros:
|
||||
* Enables us to send key-down and key-up keys independently
|
||||
* Enables us to send modifiers on their own
|
||||
* Part of the VT 510 standard
|
||||
|
||||
#### Cons:
|
||||
* neither `xterm` nor `gnome-terminal` (VTE) seem to implement this. I'm not
|
||||
sure if anyone's got a reference implementation for us to work with.
|
||||
* Unsure how this would work with other keyboard layouts
|
||||
- [this doc](https://vt100.net/docs/vt510-rm/chapter8.html#S8.13) seems to
|
||||
list the key-down/up codes for all the en-us keyboard keys, but the
|
||||
scancodes for these are different for up and down. That would seem to
|
||||
imply we couldn't just shove the Win32 scancode in those bits
|
||||
|
||||
### `DECKPM`, `DECSMKR`
|
||||
[DECKPM](https://vt100.net/docs/vt510-rm/DECKPM.html)
|
||||
[DECSMKR](https://vt100.net/docs/vt510-rm/DECSMKR.html)
|
||||
[DECEKBD](https://vt100.net/docs/vt510-rm/DECEKBD.html)
|
||||
|
||||
#### Pros:
|
||||
* Enables us to send key-down and key-up keys independently
|
||||
* Enables us to send modifiers on their own
|
||||
* Part of the VT 510 standard
|
||||
|
||||
#### Cons:
|
||||
* neither `xterm` nor `gnome-terminal` (VTE) seem to implement this. I'm not
|
||||
sure if anyone's got a reference implementation for us to work with.
|
||||
* not sure that "a three-character ISO key position name, for example C01" is
|
||||
super compatible with our Win32 VKEYs.
|
||||
|
||||
|
||||
### `libtickit` encoding
|
||||
[Source](http://www.leonerd.org.uk/hacks/fixterms)
|
||||
|
||||
#### Pros:
|
||||
* Simple encoding scheme
|
||||
|
||||
#### Cons:
|
||||
* Doesn't differentiate between keydowns and keyups
|
||||
* Unsure who implements this - not extensively investigated
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
* The initial discussion for this topic was done in [#879], and much of the
|
||||
research of available options is also available as a discussion in [#4999].
|
||||
* [Why Is It so Hard to Detect Keyup Event on Linux?](https://blog.robertelder.org/detect-keyup-event-linux-terminal/)
|
||||
- and the [HackerNews discussion](https://news.ycombinator.com/item?id=19012132)
|
||||
* [ConEmu specific OSCs](https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC)
|
||||
* [iterm2 specific sequences](https://www.iterm2.com/documentation-escape-codes.html)
|
||||
* [terminal-wg draft list of OSCs](https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/10)
|
||||
|
||||
<!-- Footnotes -->
|
||||
[#530]: https://github.com/microsoft/terminal/issues/530
|
||||
[#879]: https://github.com/microsoft/terminal/issues/879
|
||||
[#1119]: https://github.com/microsoft/terminal/issues/1119
|
||||
[#1694]: https://github.com/microsoft/terminal/issues/1694
|
||||
[#2865]: https://github.com/microsoft/terminal/issues/2865
|
||||
[#3079]: https://github.com/microsoft/terminal/issues/3079
|
||||
[#3483]: https://github.com/microsoft/terminal/issues/3483
|
||||
[#3608]: https://github.com/microsoft/terminal/issues/3608
|
||||
[#4334]: https://github.com/microsoft/terminal/issues/4334
|
||||
[#4446]: https://github.com/microsoft/terminal/issues/4446
|
||||
[#4999]: https://github.com/microsoft/terminal/issues/4999
|
||||
|
||||
[`INPUT_RECORD`]: https://docs.microsoft.com/en-us/windows/console/input-record-str
|
||||
|
||||
["full mode"]: https://sw.kovidgoyal.net/kitty/protocol-extensions.html#keyboard-handling
|
||||
@@ -507,7 +507,7 @@ runtimeclass TerminalParameters {
|
||||
* [ ] Add a `ShortcutAction` for `FocusPane`, which accepts a single parameter
|
||||
`index`.
|
||||
- We'll need to track each `Pane`'s ID as `Pane`s are created, so that we can
|
||||
quickly switch to the i'th `Pane`.
|
||||
quickly switch to the nth `Pane`.
|
||||
- This is in order to support the `-t,--target` parameter of `split-pane`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
319
doc/specs/drafts/#1256 - Tab tearoff.md
Normal file
319
doc/specs/drafts/#1256 - Tab tearoff.md
Normal file
@@ -0,0 +1,319 @@
|
||||
---
|
||||
author: Michael Niksa @miniksa/miniksa@microsoft.com
|
||||
created on: 2019-07-24
|
||||
last updated: 2019-07-24
|
||||
issue id: #1256
|
||||
---
|
||||
|
||||
# Tab Tearoff/Merge & Default App IPC
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec describes the sort of interprocess communications that will be required to support features like tab tearoff and merge. It goes through some of the considerations that became apparent when I tried to prototype passing connections between `conhost` and `wt`.
|
||||
|
||||
## Inspiration
|
||||
|
||||
Two main drivers:
|
||||
1. We want the ability to tear off a tab from one Windows Terminal instance and send it to another Windows Terminal instance
|
||||
2. We want the ability for a launch of a command-line application to trigger a hosting environment that isn't the stock in-box `conhost.exe`.
|
||||
|
||||
Both of these concerns will require there to exist some sort of interprocess communication manager that can send/receive the system handles representing connections between client applications and the hosting environment.
|
||||
|
||||
I spent some time during the Microsoft Hackathon in July 2019 investigating these avenues with a branch I pushed and linked at the bottom. The work resulted in me finding more questions than answers and ultimately deciding that a Hackathon is good enough for exploration of the mechanisms and ideas behind this, but not a good time for a full implementation.
|
||||
|
||||
## Solution Design
|
||||
|
||||
### Common Pieces
|
||||
|
||||
There are several common pieces needed for both the tab tear-off scenario and the default application scenario.
|
||||
|
||||
#### Manager
|
||||
|
||||
We need some sort of server/manager code that sits there waiting for connections from `wt.exe` processes and potentially `conhost.exe` processes such that it can broker a connection between the processes. It either needs to run in its own process or it needs to run in one of the existing `wt.exe`s that is chosen as the primary manager at the time. It should create communication channels and a global mutex at the time of creation.
|
||||
|
||||
All other `wt.exe` processes starting after the primary should detect the existence of the server manager process and wait on the mutex handle. When the primary disappears, the OS scheduler should choose one of the others to wake up first on the mutex. It can take the lock and then set up the primary management channel.
|
||||
|
||||
Alternatively, if the manager process is completely isolated and we expect all `wt.exe`s to have to remain connected at all times, we can make it such that when the connections are broken between the individual processes and the manager that they all shut down. I would prefer that it is resilient (the previous option) over this one, but browsers must have a good reason for preferring this way.
|
||||
|
||||
I attempted one particular way in a prototype of communicating between processes by setting up a Multithreaded Pipe Server using a Message-type configuration. This is visible in the branch I linked at the bottom. However, ultimately I think we would want to formalize around something more structured, tested, and inherently secured like a COM server interface.
|
||||
|
||||
#### Connection details
|
||||
|
||||
There are several parameters to a connection and several different modes. In short, they summarize to the ability to pass kernel handles between two processes and/or the ability to pass arbitrary length structured information about paths and settings. Both tab tear off and default application will likely need both functionalities.
|
||||
|
||||
##### Fresh Start
|
||||
For an application that is being freshly started, the information required to begin the session is one of three things:
|
||||
1. A server (and maybe reference) handle that describes the driver connection between the console server and the command-line client process. A `conhost.exe` can wrap this and turn it into a PTY. This may also contain LNK file (shortcut file) preferences for the running session.
|
||||
1. A command-line string and working directory that describes which command-line client process we want to start. A `conhost.exe` can start this up and create the server and reference handles along the way and then turn it into a PTY.
|
||||
1. A PTY session with its read, write, and signal handles.
|
||||
|
||||
When transiting a connection, we need to be aware of all three of these modes and relay them to the destination `wt.exe`.
|
||||
|
||||
For system handles, we can use the manager to broker a request to the destination process to find its `PID` and tell the source process. We can then use the `PID` with the `OpenProcess` method and the `PROCESS_DUP_HANDLE` right to get a handle to `DuplicateHandle` any of the above handle types into the destination process. The act of opening and duplicating the handles already requires the OS to check our access tokens and rights to interfere with another process, so that should automatically handle some level of the security checking for us.
|
||||
|
||||
For command-line string and working directory, we can pass all of this information along to the destination `wt.exe` and let it attempt to start a new ConPTY normally as if someone had chosen to start an option from the dropdown menu. A minor trick here is that we may need to attempt to match the command-line string with one of the user profiles to line up the icon and user-preferences for how the session should launch.
|
||||
|
||||
Lastly, for things started from an LNK, a user might expect that a window launched inside `wt.exe` from an old shortcut that they had would still apply even if that shortcut's properties technically apply to `conhost.exe` preferences and not to `wt.exe` preferences. The behavior here would likely to be to transit the LNK file information along to the `wt.exe` process by the same mechanism as a command-line string or working directory and let `wt.exe` use the shortcut parsing shared libraries to extract this information and migrate it into a `Settings` preference. Whether we would store that `Settings` preference or not for future use in the drop down might be an option or a prompt.
|
||||
|
||||
##### Already Running
|
||||
For an application that is already running, we will need to send several pieces of information to successfully migrate to a new tab location:
|
||||
1. The ConPTY handles for read, write, and signal
|
||||
1. The scroll-back history that is stored inside `wt.exe` but isn't actually a part of what the underlying PTY-mode `conhost.exe` re-renders at any given time
|
||||
1. The user preferences and session information related to `Settings`.
|
||||
|
||||
We would send all of this over to the destination by whatever IPC mechanism and then let it stand up a new tab with all of the same parameters as the tab on the other end.
|
||||
|
||||
**ALTERNATIVELY**
|
||||
|
||||
If we move everything to an isolated process model where the individual tabs/panes have a process and their UI is hosted in another frame/shell process and then there's a manager process, we will presumably already have to architect a solution that allows the UIs to be remoted onto other interfaces (Component UI?). If this is true, then all we need to relay for an active session is the information required to redirect the drawing/input targets for a given tab/pane to a different shell. This may ultimately be easier and more reliable than moving and rebuilding all the pieces of what fundamentally makes a session to the other side.
|
||||
|
||||
### Separate Pieces
|
||||
|
||||
#### For Tab Tear-off
|
||||
|
||||
We add a handler to the on-drag for the tab bar. We also likely need to implement a drag and drop handler. Drag and drop handlers use OLE (COM) so this might be another reason why we should implement the entire manager as COM. Note, I have never used this before so this is a theoretical low-knowledge design that would have to be explored...
|
||||
|
||||
Presumably the tab control from WinUI will update to support reordering the tabs through its own drag/drop. But we would likely want to create some sort of drag source with the session GUID when a drag operation starts.
|
||||
|
||||
Then we can let the OS handle the drop operation with the session GUID information. If the drop handler drops onto another wt.exe, it can use the session GUID in the drop payload in order to convey connection information between the processes. If it drops somewhere else, presumably we can be made aware of that in the source of the drag/drop operation and instead spawn a new `wt.exe` with arguments that specify that it should start up doing the "drop" portion of the operation to the session GUID with the manager instead of launching the default tab.
|
||||
|
||||
#### For Default Application
|
||||
|
||||
For default application launches, `conhost.exe` will have to attempt to transfer the incoming connection to the registered terminal handler instead of launching its own UI window.
|
||||
|
||||
If the registered handler fails to start the connection, there is no registered handler, or any part of this mechanism fails. The `conhost.exe` needs to fall back to doing whatever it would have done prior to this development (launching a window if necessary, being hidden, etc.)
|
||||
|
||||
##### Interactive vs. Not
|
||||
|
||||
We would have to be able to detect the difference between an interactive and non-interactive mode here.
|
||||
- Interactive is defined as the end-user is attempting to launch a command-line application with a visible window to see the output and enter input.
|
||||
- Non-interactive is defined as tools, utilities, and services attempting to launch a command-line application with no visible window (and possibly some redirected handles).
|
||||
|
||||
We do not want to capture non-interactive sessions as compilers, scripts, and utilities run command-line tools all the time. These should not trigger the overhead of being transitioned into the terminal as they will not need output or display.
|
||||
|
||||
Additionally, we may need to identify ConPTYs being started and ensure that they don't accidentally attempt to hand off in an infinite loop.
|
||||
|
||||
The biggest trick here is that we don't know whether it is going to be interactive or not until we begin to accept the connection from the server handle. We have two choices here:
|
||||
|
||||
##### Inbox conhost handles it
|
||||
The inbox `conhost.exe` can just accept the connection from the server handle, assure itself that a `wt.exe` could take over the UI hosting of the session, and then switch itself into `ConPTY` mode and give those handles over to `wt.exe` and remain invisible in the background in PTY mode (much the same as if `wt.exe` had started the connection itself).
|
||||
|
||||
The upside here is that most of the startup connection flow happens normally, the `conhost.exe` that was given the server handle is the one that will continue to service it for the lifetime of the command-line application session. I can then discard any concerns about how the driver reacts and how the applications grovel for the relationship between processes as it will be normal.
|
||||
|
||||
The downside here is that launching command-line applications from shortcuts, the shell, or the run box (as is what triggers the default application scenario) will be using an old version of the PTY. It is possible and even probable that we will make improvements to the PTY that we would want to leverage if they're on the system already inside the app package. However, if we try to transit the server connection to the PTY in the package, we will have to deal with:
|
||||
1. Potentially leaving the original conhost.exe open until the other one exits in case someone is waiting on the process
|
||||
1. Coming up with some sort of dance to have the delegated PTY conhost inside the package determine the interactivity on starting the connection **OR** having the outside conhost start the connection and passing the connection off part way through if it's interactive **OR** something of that ilk.
|
||||
|
||||
##### Conhost in the Terminal package handles it
|
||||
We could just send the server connection from the `conhost.exe` in System32 into the one inside the package and let it deal with it. We can connect to the broker and pass along the server handle and let `wt.exe` create a `conhost.exe` in PTY mode with that specific server handle.
|
||||
|
||||
The upsides/downsides here are exactly opposite of those above, so I won't restate.
|
||||
|
||||
##### Making default app work on current and downlevel OS
|
||||
There's a few areas to study here.
|
||||
|
||||
1. Replacing conhost.exe in system32 at install time
|
||||
- The OS (via the code for `ConsoleInitialize` inside `kernelbase.dll`) will launch `C:\windows\system32\conhost.exe` to start a default application session with the server handle. We can technically replace this binary in `system32` with an `OpenConsole.exe` named `conhost.exe` to make newer code run on older OS (presuming that we have the CRTs installed, build against the in-OS-CRT, and otherwise have conditional feature detection properly performed for all APIs/references not accessible downlevel). This is how we test/develop locally inside Windows without a full nightly build, so we know it works to some degree. Replacing a binary in `system32` is a bit of a problem, though, because the OS actively works to defend against this through ACLs (Windows File Protection which detected and restored changes here is gone, I believe). Additionally, it works for us because we're using internal builds and signing our binaries with test certificates for which our machines have the root certificate installed. Not going to cut it outside. We probably also can't sign it officially with the app signing mechanism and have it work because I'm not sure the root certificates for app signing will be trusted the same way as the certificates for OS signing. Also, we can't build outside of Windows against the in-box CRT. So we'd have to have the MSVCRT redist, which is also gross.
|
||||
|
||||
2. Updating kernelbase.dll to look up the launch preference and/or to launch a console host via a protocol handler
|
||||
- To make this work anywhere but the most recent OS build, we'd have to service downlevel. Given `kernelbase.dll` is fundamental to literally everything, there's virtually no chance that we would be allowed to service it backwards in time for the sake of adding a feature. It's too risky by any stretch of the imagination. It's even risky to change `kernelbase.dll` for an upcoming release edition given how fundamental it is. End of thought experiment.
|
||||
|
||||
3. Updating conhost.exe to look up the launch preference and/or to launch another console host via a protocol handler
|
||||
- This would allow the `C:\windows\system32\conhost.exe` to effectively delegate the session to another `conhost.exe` that is hopefully newer than the inbox one. Given that the driver protocol in the box doesn't change and hasn't changed and we don't intend to change it, the forward/backward compatibility story is great here. Additionally, if for whatever reason the delegated `conhost.exe` fails to launch, we can just fall back and launch the old one like we would have prior to the change. It is significantly more likely, but still challenging, to argue for servicing `conhost.exe` back several versions in Windows to make this light up better for all folks. It might be especially more possible if it is a very targeted code snippet that can drop in to all the old versions of the `conhost.exe` code. We would still have the argument about spending resources developing for OS versions that are supposed to be dropped in favor of latest, but it's still a lesser argument than upending all of `kernelbase.dll`.
|
||||
- A protocol handler is also well understood and relatively well handled/tested in Windows. Old apps can handle protocols. New apps can handle protocols. Protocol handlers can take arguments. We don't have to lean on any other team to get them to help change the way the rest of the OS works.
|
||||
|
||||
#### Communicating the launch
|
||||
For the parameters passing, I see a few options:
|
||||
1. `conhost.exe` can look up the package registration for `wt.exe` and call an entrypoint with arguments. This could be adapted to instead look up which package is registered as the default one instead of `wt.exe` for third party hosts. We would have to build provisions into the OS to select this, or use some sort of publically documented registry key mechanism. Somewhat gross.
|
||||
1. `conhost.exe` can call the execution alias with parameters. WSL distro launchers use this.
|
||||
1. We can define a protocol handler for these sorts of connections and let `wt.exe` register for it. Protocol handlers are already well supported and understood both by classic applications and by packaged/modern applications on Windows. They must have provisions to communicate at least some semblance of argument data as well. This is the route I'd probably prefer. `ms-term://incoming/<session-id>` or something like that. The receiving `wt.exe` can contact the manager process (or set one up if it is the first) and negotiate receiving the session that was specified into a new tab.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
### For Tab Tear-off
|
||||
|
||||
#### Ideal World
|
||||
The UX would be just as one might expect from a browser application.
|
||||
|
||||
- Mouse down and drag on a tab should provide some visual indication that it is being dragged.
|
||||
- Dragging left/right should provide a visual indicator of the tabs reordering on the bar and otherwise not involve the IPC manager service.
|
||||
- Dragging up/down to break free from the tab bar should launch a new instance of `wt.exe` passing in the state of the dragging tab as the initial launch point (ignoring other default launch aspects). The drag/mouse-down would be passed to that new instance which would chase the mouse.
|
||||
- Continuing to drag the loose tab onto the tab bar of another running instance of `wt.exe` would merge the tab with that copy of the application. The interim new/loose frame instance of `wt.exe` would close when it transferred out the last tab to the drop location.
|
||||
|
||||
#### Simplified V1
|
||||
To simplify this for a first iteration, we could just make it so the transfer does not happen live.
|
||||
- Mouse down and drag on a tab should provide a visual indication that it is being dragged by changing the cursor (or something of that ilk)
|
||||
- Nothing would actually happen in terms of transitioning the tab until it is released
|
||||
- If released onto the same `wt.exe` instance in a different spot on the tab bar, we reorder the tabs in the tab control
|
||||
- If released onto a different `wt.exe` instance, we relay the communications channel and details through the IPC manager to the other instance. It opens the tab on the destination instance; we close the tab on the source instance.
|
||||
- If released onto anything that isn't a `wt.exe` instance, we create a new `wt.exe` instance and send in the connection as the default startup parameter.
|
||||
|
||||
#### Component UI
|
||||
It is also theoretically possible that if we could find a Component UI style solution (where the tab/panes live in their own process and just remote the UI/input into the shell) that it would be easy and even trivial to change out which shell/frame host is holding that element at any given time.
|
||||
|
||||
### For Default Application
|
||||
The UX would make it look exactly like the user had started `wt.exe` from a shortcut or launch tile, but would launch the first tab differently than the defaults.
|
||||
|
||||
#### No WT already started
|
||||
If no `wt.exe` is already started, the `conhost.exe` triggered by the system to host the client application would find the installed `wt.exe` package and launch it with parameters to use as its first connection (in lieu of launching the default tab). `conhost.exe` wouldn't show a window, it would drop into ConPTY mode and only the new `wt.exe` and its tab would be visible.
|
||||
|
||||
#### WT already started
|
||||
If a `wt.exe` is already started, `conhost.exe` would find the running instance and just add a new tab at the end of the tab bar by the same mechanism.
|
||||
|
||||
#### Multiple WTs already started
|
||||
If multiple `wt.exe`s are already started, `conhost.exe` would have to find the foreground one, the active one, or the primary/manager one and send the tab there. I'm not sure how other tabbing things to do this. We could research/study.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
I don't believe it changes anything for accessibility. The only concern I'd have to call out is the knowledge I have that the UIA framework makes its connections and some of its logic/reasoning based on PIDs, HWNDs, and the hierarchy thereof. Playing with these might impact the ability of screen reading applications to get the UIA tree when tabs have been shuffled around.
|
||||
|
||||
### Security
|
||||
|
||||
This particular feature will have to go through a security review/audit. It is unclear what level of control we will need over the IPC communication channels. A few things come to mind:
|
||||
1. We need to ensure that the mutexes/pipes/communications are restricted inside of one particular session to one particular user. If another user is also running WT in their session, it should involve a completely different manager process and system objects.
|
||||
1. We MAY have to enforce a scenario where we inhibit cross-integrity-level connections from being passed around. Generally speaking, processes at a higher integrity level have the authority to perform actions on those with a lower integrity level. This means that an elevated `wt.exe` could theoretically send a tab to a standard level `wt.exe`. We may be required to inhibit/prohibit this. We may also need to have one manager per integrity level.
|
||||
1. I'm not sure what sorts of ACL/DACL/SACLs we would need to apply to all the kernel objects involved.
|
||||
1. My initial prototype here used message-passing type pipes with a custom rolled protocol. If I make my own protocol, it needs to be fuzzed. And I'm probably missing something. Many/most of these concerns for security are probably eliminated if we use a well-known mechanism for this sort of IPC. My thoughts go to a COM server. More complicated to implement than message pipes, but probably brings a lot of security benefits and eliminates the need to fuzz the protocol (probably).
|
||||
|
||||
### Reliability
|
||||
|
||||
In the simple implementation, it will decrease reliability. We'll be shuffling connections back and forth between application instances. By default, that's more risky than leaving things alone. The only reason it is worth it is the user experience.
|
||||
|
||||
We might be able to mitigate some of the reliability concerns here or even improve reliability by going a step further with the process/containerization model like browsers do and standing up each individual tab as its own process host.
|
||||
|
||||
```
|
||||
wt.exe - Manager Mode
|
||||
|- wt.exe - Frame Host Mode
|
||||
| |- wt.exe - Tab Host Mode
|
||||
| | |- conhost.exe - ConPTY mode
|
||||
| | |- pwsh.exe - Client application
|
||||
| |- wt.exe - Tab Host Mode
|
||||
| |- conhost.exe - ConPTY mode
|
||||
| |- cmd.exe - Client application
|
||||
|- wt.exe - Frame Host Mode
|
||||
|- wt.exe - Tab Host Mode
|
||||
|- conhost.exe - ConPTY mode
|
||||
|- pwsh.exe - Client application
|
||||
```
|
||||
|
||||
The current structure of `wt.exe` has everything hosted within the one process. To improve reliability, we would likely have to make `wt.exe` run in three modes.
|
||||
1. Manager Mode - no UI, just sits there as a broker to hold the kernel objects for a given window station/session and integrity level, accepts protocol handler routines, helps relay connections between various frame hosts when tabs move and determines where to instantiate new default-app tabs
|
||||
1. Frame Host Mode - The complete outer shell of the application outside of an individual tab. Hosts the tab bar, settings drop downs, title bar, etc.
|
||||
1. Tab Host Mode - The inner shell of an individual tab including the rendering area, scroll bar, inputs, etc.
|
||||
1. Pane Host Mode - Now that panes are a thing, we might need to go even one level deeper. Or maybe it's just a recursion on Tab Host mode.
|
||||
|
||||
How these connect to each other is unexplored at this time.
|
||||
|
||||
### Compatibility
|
||||
|
||||
There are a few compatibility concerns here, primarily related to how client applications or outside utilities detect the relationship between a command-line client application and its console hosting environment.
|
||||
|
||||
We're well aware that the process tree/hierarchy is one of the major methods used for understanding the relationship between the client and server application. However, in order to accomplish our goals here, it is inevitable that the original hosting `conhost.exe` (either started in ConPTY mode by a `wt.exe` or started by the operating system in response to an otherwise unhosted command-line application) will become orphaned or otherwise disassociated with the UI that is actually presenting it.
|
||||
|
||||
It is possible (but would need to be explored) that the APIs available to us to reorder the parenting of the processes to put the `conhost.exe` as the parent of the `cmd.exe` (despite the fact that `cmd.exe` usually starts first as the default application and the `ConsoleInitialize` routines inside `kernelbase.dll` create the `conhost.exe`) could be reused here to shuffle around the parent/child relationships. However, it could also introduce new problems. One prior example was that the UIA trees for accessibility do **NOT** tolerate the shuffling of the parent child relationship because their communication channel sessions are often tied to the relationships of HWNDs and PIDs.
|
||||
|
||||
#### Hierarchy Example between two Terminals (tab tearoff/merge)
|
||||
|
||||
In the one instance, we have this process hierarchy. Two instances of Windows Terminal exist. In Terminal A, the user has started a `cmd.exe` and a `pwsh.exe` tab. In the second instance, the user has started just one `cmd.exe` tab.
|
||||
|
||||
```
|
||||
- wt.exe (Terminal Instance A)
|
||||
|- conhost.exe (in PTY mode) - Hosted to A
|
||||
| |- cmd.exe
|
||||
|- conhost.exe (in PTY mode) - Hosted to A
|
||||
|- pwsh.exe <-- I will be dragged out
|
||||
|
||||
- wt.exe (Terminal Instance B)
|
||||
|- conhost.exe (in PTY mode) - Hosted to B
|
||||
|- cmd.exe
|
||||
```
|
||||
|
||||
When the `pwsh.exe` tab is torn off from Instance A and is dropped onto Instance B, the process hierarchy doesn't actually change. The connection details, preferences, and session metadata are passed via the IPC management channels, but to an outside observer, nothing has actually changed.
|
||||
|
||||
```
|
||||
- wt.exe (Terminal Instance A)
|
||||
|- conhost.exe (in PTY mode) - Hosted to A
|
||||
| |- cmd.exe
|
||||
|- conhost.exe (in PTY mode) - Hosted to B
|
||||
|- pwsh.exe <-- I am hosted in B but I'm parented to A
|
||||
|
||||
- wt.exe (Terminal Instance B)
|
||||
|- conhost.exe (in PTY mode) - Hosted to B
|
||||
|- cmd.exe
|
||||
```
|
||||
|
||||
I don't believe there are provisions in the Windows OS to reparent applications to a different process.
|
||||
|
||||
Additionally, this becomes more interesting when Terminal Instance A dies and B is still running:
|
||||
|
||||
```
|
||||
- conhost.exe (in PTY mode) - Hosted to B
|
||||
|- pwsh.exe <-- I am hosted in B but I'm parented to A
|
||||
|
||||
- wt.exe (Terminal Instance B)
|
||||
|- conhost.exe (in PTY mode) - Hosted to B
|
||||
|- cmd.exe
|
||||
```
|
||||
|
||||
When instance A dies, the `conhost.exe` that was reparented keeps running and now just appears orphaned within the process hierarchy, reporting to the top level under utilities like Process Explorer.
|
||||
|
||||
I believe the action plan here would be to implement what we can, observe the state of the world, and correct going forward. We don't have a solid understanding of how many client applications might be impacted by this apparent change. It also might be perfectly OK because the client applications will always remain parented to the same `conhost.exe` even if those `conhost.exe`s don't report up to the correct `wt.exe`.
|
||||
|
||||
It is also unclear whether someone might want to write a utility from the outside to discover this hierarchy. I would be inclined to not provide a way to do this without a strong case otherwise because attempting to understand the local machine process hierarchy is a great way to box yourself in when attempting to expand later to encompass remote connections.
|
||||
|
||||
#### Hierarchy Example between Conhost and a Terminal (default application)
|
||||
|
||||
This looks very much like the previous section where Terminal Instance B died.
|
||||
|
||||
```
|
||||
- conhost.exe (in PTY mode) - Hosted to A
|
||||
|- pwsh.exe
|
||||
|
||||
- wt.exe (Terminal Instance A)
|
||||
```
|
||||
|
||||
The `conhost.exe` was started in response to a `pwsh.exe` being started with no host. It then put itself into PTY mode and launched into a connection of `wt.exe` instance A.
|
||||
|
||||
**ALTERNATIVELY**
|
||||
|
||||
```
|
||||
- conhost.exe - idling
|
||||
|
||||
- wt.exe (Terminal Instance A)
|
||||
|- conhost.exe (in PTY mode)
|
||||
|- pwsh.exe
|
||||
```
|
||||
|
||||
The `conhost.exe` at the top was launched in response to `pwsh.exe` being started with no host. It identified that `wt.exe` was running and instead shuttled the incoming connection into that `wt.exe`. `wt.exe` stood up the `conhost.exe` in PTY mode beneath itself and the client `pwsh.exe` call below that. The PTY mode `conhost.exe` uses its reparenting commands on startup to make the tree look like the above. The orphaned (originally started) `conhost.exe` waits until the connection exits before exiting itself in case someone was waiting on it.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
This is obviously less efficient than not doing it as we have to stand up servers and protocols and handlers for shuffling things about.
|
||||
|
||||
But as long as we're creating threads and services that sleep most of the time and are only awakened on some kernel/system event, we shouldn't be wasting too much in terms of power and background resources.
|
||||
|
||||
Additionally, `wt.exe` is worse than `conhost.exe` alone in all efficiency categories simply because it not only requires more resources to display in a "pretty" manner, but it also requires a `conhost.exe` under it in PTY mode to adapt the API calls. This is generally acceptable for end users who care more about the experience than the total performance.
|
||||
|
||||
It is, however, not likely to be much if any worse than just choosing to use `wt.exe` anyway over `conhost.exe`.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
I've listed most of the issues above in their individual sections. The primary highlights are:
|
||||
1. Process tree layout - The processes in hierarchy may not make sense to someone inspecting them either visually with a tool or programmatically
|
||||
1. Process and kernel object lifetime - Applications may be counting on a specific process or object lifetime in regards to their hosting window and we might be tampering with that in how we apply job objects or shuffle around ownership to make tabs happen
|
||||
1. Default launch expectations - It is possible that test utilities or automation are counting on `conhost.exe` being the host application or that they're not ready to tolerate the potential for other applications to start. I think the interactive/non-interactive check mitigates this, but we'd have to remain concerned here.
|
||||
1. `AttachConsole` and `DetachConsole` and `AllocConsole` - I don't have the slightest idea what happens for these APIs. We would have to explore. `AttachConsole` has restrictions based on the process hierarchy. It would likely behave in interesting ways with the strange parenting order and might be a driver to why we would have to adjust the parenting of the processes (or change the API under the hood). `DetachConsole` might create an issue where a tab disappears out of the terminal and the job object causes everything to die. `AttachConsole` wouldn't necessarily be guaranteed to go back into the same `wt.exe` or a `wt.exe` at all.
|
||||
|
||||
## Future considerations
|
||||
|
||||
This might unlock some sort of isolation for extensions as well. Extensions of some sort our on our own long term roadmap, but they're inherently risky to the stability and integrity of the application. If we have to go through a lot of gyrations to enable process containerization and an interprocess communication model for tab tear off and default application work, we might also be able to contain extensions the same way. This derives further from the idea of what browsers do.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Manager Prototype](https://github.com/microsoft/terminal/blob/dev/miniksa/manager/src/types/Manager.cpp)
|
||||
- [Pipe Server documentation](https://docs.microsoft.com/en-us/windows/win32/ipc/multithreaded-pipe-server)
|
||||
- [OLE Drag and Drop](https://docs.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-registerdragdrop)
|
||||
- [OpenProcess](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess)
|
||||
- [DuplicateHandle](https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle)
|
||||
115
doc/specs/drafts/#997 Non-Terminal-Panes.md
Normal file
115
doc/specs/drafts/#997 Non-Terminal-Panes.md
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2019-05-31
|
||||
last updated: 2019-05-31
|
||||
issue id: #997
|
||||
---
|
||||
|
||||
# Non-Terminal Panes
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec hopes to cover the work necessary to enable panes to host non-terminal
|
||||
content. It'll describe changes to the `Pane` class to support hosting arbitrary
|
||||
controls in addition to terminals.
|
||||
|
||||
## Inspiration
|
||||
|
||||
The primary driver for this change is to enable testing of the pane code. If a
|
||||
`Pane` can host an arbitrary class, then a use case for that would be the
|
||||
hosting of a non-xaml test class that acts like a control. This test class could
|
||||
be have its state queried, to make sure that the panes are properly delivering
|
||||
focus to the correct pane content.
|
||||
|
||||
Additionally, non-terminal panes could be used to host a variety of other
|
||||
content, such as browser panes, sticky notes, text editor scratch-pads, etc.
|
||||
Some discussion of these ideas are in #644.
|
||||
|
||||
## Solution Design
|
||||
|
||||
We'll change the TermControl class to derive from the
|
||||
`Windows.UI.Xaml.Controls.Control` runtime class.
|
||||
* We may need to override its `FocusState` and `Focus` methods, and implement
|
||||
them by plumbing them straight through to the fake Control the `TermControl`
|
||||
hosts.
|
||||
* Otherwise, it might be possible that we could just remove that fake control
|
||||
entirely.
|
||||
* We'll remove the `GetControl` method from the `TermControl`, as the
|
||||
`TermControl` itself will now be used as the control.
|
||||
|
||||
We'll change the Pane class to accept a `Windows.UI.Xaml.Controls.Control`
|
||||
instead of a `TermControl`.
|
||||
|
||||
We'll additionally change the `Pane` constructor to accept an `optional<GUID>`
|
||||
as opposed to needing a GUID. For constructing panes with Terminals, we should
|
||||
pass a GUID corresponding to that settings profile. For panes that aren't
|
||||
hosting terminals however, we should pass `nullopt` as the GUID. For panes that
|
||||
are leaf nodes (panes which are hosting a control, not another pair of panes),
|
||||
if the pane has a GUID, we can assume that the control is a TermControl.
|
||||
|
||||
When we want to host other types of content, we'll simply pass any other control
|
||||
to the Pane, and it'll render it just as it would the `TermControl`.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
Instead of a pane hosting a terminal, it could host _any arbitrary control_. The
|
||||
control would still be subject to the sizing provided to it by the `Pane`, but
|
||||
it could host any arbitrary content.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Security
|
||||
|
||||
I don't forsee this implementation by itself raising security concerns. This
|
||||
feature is mostly concerned with adding support for arbitrary controls, not
|
||||
actually implementing some arbitrary controls.
|
||||
|
||||
### Reliability
|
||||
|
||||
With more possible controls in a pane than just a terminal, it's possible that
|
||||
crashes in those controls could impact the entire Terminal app's reliability.
|
||||
This would largely be out of our control, as we only author the TermControl.
|
||||
|
||||
We may want to consider hosting each pane in it's own process, similar to how
|
||||
moder browsers will host each tab in its own process, to help isolate tabs. This
|
||||
is a bigger discussion than the feature at hand, however.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
decide to host a WebView in a pane, then it surely could impact these measures.
|
||||
I don't believe this will have a noticable impact _on its own_. Should the user
|
||||
However, I leave that discussion to the implementation of the actual alternative
|
||||
pane content itself.
|
||||
|
||||
### Accessibility
|
||||
|
||||
When implementing the accessibility tree for Panes, we'll need to make sure that
|
||||
for panes with arbitrary content, we properly activate their accessibility,
|
||||
should the control provide some sort of accessibility pattern.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
* [ ] It's entirely possible that panes with non-terminal content will not be
|
||||
able to activate keybindings from the terminal application. For example, what
|
||||
if the hosted control wants to use Ctrl+T for its own shortcut? The current
|
||||
keybindings model has the `TermControl` call into the App layer to see if a
|
||||
keystroke should be handled by the app first. We may want to make sure that
|
||||
for non-terminal controls, we add a event handler to try and have the
|
||||
`AppKeyBindings` handle the keypress if the control doesn't. This won't solve
|
||||
the case where the control wants to use a keybinding that is mapped by the
|
||||
Terminal App. In that case, non-terminal controls will actually behave
|
||||
differently from the `TermControl`. The `TermControl` will give the app the
|
||||
first chance to handle a keybinding, while for other controls, the app will
|
||||
give the control the first chance to handle the keypress. This may be mildly
|
||||
confusing to end users.
|
||||
|
||||
## Future considerations
|
||||
|
||||
I expect this to be a major part of our (eventual) extensibility model. By
|
||||
allowing arbitrary controls to be hosted in a Tab/Pane, this will allow
|
||||
extension authors to embed their own UI experiences alongside the terminal.
|
||||
See #555 for more discussion on the extensibility/plugin subject.
|
||||
|
||||
## Resources
|
||||
|
||||
N/A
|
||||
127
doc/specs/drafts/576-ProfilesJumplistSpec.md
Normal file
127
doc/specs/drafts/576-ProfilesJumplistSpec.md
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
author: Brandon Chong @dsafa
|
||||
created on: 2019-06-20
|
||||
last updated: 2019-06-20
|
||||
issue id: 576
|
||||
---
|
||||
|
||||
# Add Profiles To Jumplist
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec outlines adding support for launching a profile from the jumplist. The main two points that will be detailed are:
|
||||
1. Adding jumplist support
|
||||
2. Adding the ability to open a profile from the jumplist
|
||||
|
||||
The profiles available in the jumplist will match the profiles available in the application from the + dropdown. The scope of the jumplist feature is limited to desktop devices.
|
||||
|
||||
## Inspiration
|
||||
The inspiration is to be able to quickly open the terminal with one of the configured profiles.
|
||||
|
||||
## Terminology
|
||||
- Jumplist: This is the menu that appears when the application is right-clicked on the taskbar or the start menu.
|
||||
|
||||
## Solution Design
|
||||
|
||||
### Overview
|
||||
The jumplist has to be created/modified during the life-cycle of the application. The following is an outline of the steps to be performed (more details below)
|
||||
1. Load settings / profiles
|
||||
2. Create the jumplist with the initial profiles
|
||||
3. Maintain sync between the jumplist and profiles throughout the life of the application
|
||||
|
||||
### Jumplist integration
|
||||
UWP provides an API to access to the jumplist through the [Windows.UI.StartScreen.JumpList class](https://docs.microsoft.com/en-us/uwp/api/windows.ui.startscreen.jumplist), however from previous attempts [1], the api does not work for the project architecture.
|
||||
Instead, we'll use the COM interface [ICustomDestinationList](https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-icustomdestinationlist) [2] directly to create the jumplist. Since we are using Win32 apis, the work should be done in the `WindowsTerminal` project.
|
||||
|
||||
Using `ICustomDestinationList` is straightforward with a few additions discussed in this section [below](#Implementation-notes). Resources for using the jumplist can be found [here](https://msdn.microsoft.com/en-us/library/windows/desktop/gg281362.aspx) and [here](https://www.codeproject.com/Articles/36561/Windows-7-Goodies-in-C-Jump-Lists).
|
||||
|
||||
The basic overview:
|
||||
1. Get an instance of the `ICustomDestinationList` COM object
|
||||
2. Call `ICustomDestinationList.BeginList`
|
||||
3. Create an `IObjectCollection` COM object
|
||||
4. Create `IShellLink` COM objects for each profile and add them to the `IObjectCollection`
|
||||
5. Add the `IObjectCollection` to the `ICustomDestinationList`
|
||||
6. Call `ICustomDestination.CommitList`
|
||||
|
||||
Note that the jumplist is replaced each time, instead of being updated.
|
||||
|
||||
#### Approach
|
||||
One approach is to wrap the COM interfaces in a jumplist class and use that to add and remove items.
|
||||
|
||||
#### What if there are multiple instances of the terminal application?
|
||||
There maybe multiple instances trying to update the jumplist at the same time.
|
||||
|
||||
### Adding profiles to the jumplist
|
||||
To add the profiles to the jumplist there are some prerequisites that are needed
|
||||
- Be able to access settings / profiles from the `WindowsTerminal` project.
|
||||
- Detect when profiles are modified
|
||||
- Created: Add a new profile to the jumplist when a new profile is created by the user
|
||||
- Deleted: Remove the profile from the jumplist when the profile is removed by the user
|
||||
- Modified: We'll have to sync profile name changes / icon changes with the jump list
|
||||
|
||||
There are also different "categories" that we could add the profiles to. A jumplist can have items in a system category such as `Recent` or `Frequent` or a custom category. Jumplist items can also be a task. The difference between adding an item to a category or the task list is that items in a category can be `pinned` and `removed` from the jumplist by the user. Here is a summary of the two from the documentation [2]:
|
||||
- `Destinations can be files, folders, websites, or other content-based items, but are not necessarily file-backed. Destinations can be thought of as things or nouns`
|
||||
- `Tasks are common actions performed in an application that apply to all users of that application regardless of an individual's usage patterns. Tasks can be thought of as actions or verbs`
|
||||
|
||||
Following this, it would be more appropriate to add the profiles to the `Tasks` list as we are launching a profile. Essentially each item in the jumplist will be a shortcut that opens the terminal and provides command line arguments.
|
||||
|
||||
#### Implementation notes
|
||||
When specifying the icon to use in the `IShellLink` object, it does not appear to be able to read `ms-appx://` URIs such as ones for the default profile icons and also only able to read `.ico` files. The way that the UWP api is able to do it is by adding additional `PropertyKey`s to the `IShellLink` object [3]. Using these tools [JumpList](https://github.com/EricZimmerman/JumpList) and [Lnk Explorer](https://github.com/EricZimmerman/LECmd), we can examine what is different with UWP jumplists. When we examine the `.lnk` items that the jumplist uses, we can see that additional properties are added to the property store.
|
||||
|
||||
| Property Key (Format ID\Property ID) | Description | Example Value |
|
||||
| ---------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}\28 | App User Model DestList Provided Description | |
|
||||
| {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}\27 | App User Model DestList Provided Title | Windows PowerShell |
|
||||
| {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}\30 | App User Model DestList Provided Group Name | Profiles |
|
||||
| {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}\29 | App User Model DestList Logo Uri | ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png |
|
||||
| {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}\5 | App User Model ID | WindowsTerminalDev_8wekyb3d8bbwe!App |
|
||||
| {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}\20 | App User Model Activation Context | {61c54bbd-c2c6-5271-96e7-009a87ff44bf} |
|
||||
| {436F2667-14E2-4FEB-B30A-146C53B5B674}\100 | Link Arguments | {61c54bbd-c2c6-5271-96e7-009a87ff44bf} |
|
||||
| {F29F85E0-4FF9-1068-AB91-08002B27B3D9}\2 | (Description not available) | Windows PowerShell |
|
||||
|
||||
If we look at the property key `9f4c2855-9f79-4b39-a8d0-e1d42de1d5f3\29`, it specifies the uri for the icon and supports the `ms-appx://` scheme. So for icon support we can add that property key when creating the `IShellLink`. Also note that with this approach, it needs the `file://` uri scheme in the path for custom icons.
|
||||
|
||||
### Launching the terminal from the jumplist
|
||||
The jumplist will launch the terminal by calling the executable alias `wt.exe` with arguments to indicate the profile. The command line arguments to use are tracked in issue [#607](https://github.com/microsoft/terminal/issues/607)
|
||||
|
||||
## UI/UX Design
|
||||
No UI changes are needed. The jumplist is provided by Windows.
|
||||
|
||||
### Localization
|
||||
Depending on how the jump list items will be worded.
|
||||
|
||||
### Profile order
|
||||
The order of the profiles in the jumplist should match the order within the application.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
Will be provided by what Windows supports for jump lists
|
||||
|
||||
### Security
|
||||
Should not introduce any new security issues
|
||||
|
||||
### Reliability
|
||||
Should not introduce any new reliability issues
|
||||
|
||||
### Compatibility
|
||||
Will have to add the ability to get notified of changes to the profile settings.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
The jumplist will have to be saved each time a profile change occurs but the frequency would be expected to be low.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
#### Should it open a new instance of the terminal or open in a new tab?
|
||||
|
||||
#### What should happen if a non existant profile is launched
|
||||
The jumplist is only updated when the application is running so the profiles could be modified or deleted outside and the jumplist will not be updated. Handling will be done by whatever handles the command line parsing.
|
||||
|
||||
## Future considerations
|
||||
Other things could potentially be added to the jumplist other than profiles.
|
||||
|
||||
## Resources
|
||||
|
||||
- [1] [Original feature request: issue #576](https://github.com/microsoft/terminal/issues/576)
|
||||
- [2] [ICustomDestinationList](https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-icustomdestinationlist)
|
||||
- [3] [Windows property system](https://docs.microsoft.com/en-us/windows/desktop/properties/property-system-overview)
|
||||
110
doc/terminal-v2-roadmap.md
Normal file
110
doc/terminal-v2-roadmap.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Terminal 2.0 Roadmap
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the roadmap towards delivering Windows Terminal 2.0 by Spring 2021.
|
||||
|
||||
|
||||
## Milestones
|
||||
|
||||
The Windows Terminal project is engineered and delivered as a set of 4-week milestones. New features will go into [Windows Terminal Preview](https://aka.ms/terminal-preview) first, then a month after they been in Preview, those features will move into [Windows Terminal](https://aka.ms/terminal).
|
||||
|
||||
| Duration | Activity | Releases |
|
||||
| --- | --- | --- |
|
||||
| 2 weeks | Dev Work<br/> <ul><li>Fixes / Features for future Windows Releases</li><li>Fixes / Features for Windows Terminal</li></ul> | Release to Internal Selfhosters at end of week 2 |
|
||||
| 1 week | Quality & Stability<br/> <ul><li>Bug Fixes</li><li>Perf & Stability</li><li>UI Polish</li><li>Tests</li><li>etc.</li></ul>| Push to Microsoft Store at end of week 3 |
|
||||
| 1 week | Release <br/> <ul><li>Available from [Microsoft Store](https://aka.ms/terminal) & [GitHub Releases](https://github.com/microsoft/terminal/releases)</li><li>Release Notes & Announcement Blog published</li><li>Engineering System Maintenance</li><li>Community Engagement</li><li>Docs</li><li>Future Milestone Planning</li></ul> | Release available from Microsoft Store & GitHub Releases |
|
||||
|
||||
## Terminal Roadmap / Timeline
|
||||
|
||||
Below is the schedule for when milestones will be included in release builds of Windows Terminal and Windows Terminal Preview. The dates are rough estimates and are subject to change.
|
||||
|
||||
| Milestone End Date | Milestone Name | Preview Release Blog Post |
|
||||
| ------------------ | -------------- | ------------------------- |
|
||||
| 2020-06-30 | [1.1] in Windows Terminal Preview | |
|
||||
| 2020-07-31 | [1.2] in Windows Terminal Preview<br>[1.1] in Windows Terminal | |
|
||||
| 2020-08-31 | 1.3 in Windows Terminal Preview<br>[1.2] in Windows Terminal | |
|
||||
| 2020-09-30 | 1.4 in Windows Terminal Preview<br>1.3 in Windows Terminal | |
|
||||
| 2020-10-31 | 1.5 in Windows Terminal Preview<br>1.4 in Windows Terminal | |
|
||||
| 2020-11-30 | 1.6 in Windows Terminal Preview<br>1.5 in Windows Terminal | |
|
||||
| 2020-12-31 | 1.7 in Windows Terminal Preview<br>1.6 in Windows Terminal | |
|
||||
| 2021-01-31 | 1.8 in Windows Terminal Preview<br>1.7 in Windows Terminal | |
|
||||
| 2021-02-28 | 1.9 in Windows Terminal Preview<br>1.8 in Windows Terminal | |
|
||||
| 2021-03-31 | 1.10 in Windows Terminal Preview<br>1.9 in Windows Terminal | |
|
||||
| 2021-04-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
|
||||
| 2021-05-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
|
||||
|
||||
## Issue Triage & Prioritization
|
||||
|
||||
Incoming issues/asks/etc. are triaged several times a week, labeled appropriately, and assigned to a milestone in priority order:
|
||||
|
||||
* P0 (serious crashes, data loss, etc.) issues are scheduled to be dealt with ASAP
|
||||
* P1/2 issues/features/asks assigned to the current or future milestone, or to the [Terminal 2.0 milestone](https://github.com/microsoft/terminal/milestone/22) for future assignment, if required to deliver a 2.0 feature
|
||||
* Issues/features/asks not on our list of 2.0 features are assigned to the [Terminal Backlog](https://github.com/microsoft/terminal/milestone/7) for subsequent triage, prioritization & scheduling.
|
||||
|
||||
## 2.0 Scenarios
|
||||
|
||||
The following are a list of the key scenarios we're aiming to deliver for Terminal 2.0.
|
||||
|
||||
> 👉 Note: There are many other features that don't fit within 2.0, but will be re-assessed and prioritized for 3.0, the plan for which will be published in 2021.
|
||||
|
||||
| Priority\* | Scenario | Description/Notes |
|
||||
| ---------- | -------- | ----------------- |
|
||||
| 0 | Settings UI | A user interface that connects to settings.json. This provides a way for people to edit their settings without having to edit a JSON file.<br><br>Issue: [#1564] |
|
||||
| 0 | Command palette | A popup menu to list possible actions and commands.<br><br>Issues: [#5400], [#2046]<br>Spec: [#2193] |
|
||||
| 1 | Tab tear-off | The ability to tear a tab out of the current window and spawn a new window or attach it to a separate window.<br><br>Issue: [#1256]<br>Spec: [#2080] |
|
||||
| 1 | Clickable links | Hyperlinking any links that appear in the text buffer. When clicking on the link, the link will open in your default browser.<br><br>Issue: [#574] |
|
||||
| 1 | Default terminal | If a command-line application is spawned, it should open in Windows Terminal (if installed) or your preferred terminal<br><br>Issue: [#492]<br>Spec: [#2080] |
|
||||
| 1 | Overall theme support | Tab coloring, title bar coloring, pane border coloring, pane border width, definition of what makes a theme<br><br>Issue: [#3327]<br>Spec: [#5772] |
|
||||
| 1 | Open tab as admin/other user | Open tab in existing Windows Terminal instance as admin (if Terminal was run unelevated) or as another user.<br><br>Issue: [#5000] |
|
||||
| 1 | Traditional opacity | Have a transparent background without the acrylic blur.<br><br>Issue: [#603] |
|
||||
| 2 | SnapOnOutput, scroll lock | Pause output or scrolling on click.<br><br>Issue: [#980]<br>Spec: [#2529]<br>Implementation: [#6062] |
|
||||
| 2 | Infinite scrollback | Have an infinite history for the text buffer.<br><br>Issue: [#1410] |
|
||||
| 2 | Pane management | All issues listed out in the original issue. Some features include pane resizing with mouse, pane zooming, and opening a pane by prompting which profile to use.<br><br>Issue: [#1000] |
|
||||
| 2 | Theme marketplace | Marketplace for creation and distribution of themes.<br>Dependent on overall theming |
|
||||
| 2 | Jump list | Show profiles from task bar (on right click)/start menu.<br><br>Issue: [#576] |
|
||||
| 2 | Open with multiple tabs | A setting that allows Windows Terminal to launch with a specific tab configuration (not using only command line arguments).<br><br>Issue: [#756] |
|
||||
| 3 | Open in Windows Terminal | Functionality to right click on a file or folder and select Open in Windows Terminal.<br><br>Issue: [#1060]<br>Implementation: [#6100] |
|
||||
| 3 | Session restoration | Launch Windows Terminal and the previous session is restored with the proper tab and pane configuration and starting directories.<br><br>Issues: [#961], [#960], [#766] |
|
||||
| 3 | Quake mode | Provide a quick launch terminal that appears and disappears when a hotkey is pressed.<br><br>Issue: [#653] |
|
||||
| 3 | Settings migration infrastructure | Migrate people's settings without breaking them. Hand-in-hand with settings UI. |
|
||||
| 3 | Pointer bindings | Provide settings that can be bound to the mouse.<br><br>Issue: [#1553] |
|
||||
|
||||
Feature Notes:
|
||||
|
||||
\* Feature Priorities:
|
||||
|
||||
0. Mandatory <br/>
|
||||
1. Optimal <br/>
|
||||
2. Optional / Stretch-goal <br/>
|
||||
|
||||
[1.1]: https://github.com/microsoft/terminal/milestone/24
|
||||
[1.2]: https://github.com/microsoft/terminal/milestone/25
|
||||
[2.0]: https://github.com/microsoft/terminal/milestone/22
|
||||
[#1564]: https://github.com/microsoft/terminal/issues/1564
|
||||
[#5400]: https://github.com/microsoft/terminal/issues/5400
|
||||
[#2046]: https://github.com/microsoft/terminal/issues/2046
|
||||
[#2193]: https://github.com/microsoft/terminal/pull/2193
|
||||
[#1256]: https://github.com/microsoft/terminal/issues/1256
|
||||
[#2080]: https://github.com/microsoft/terminal/pull/2080
|
||||
[#574]: https://github.com/microsoft/terminal/issues/574
|
||||
[#492]: https://github.com/microsoft/terminal/issues/492
|
||||
[#2080]: https://github.com/microsoft/terminal/pull/2080
|
||||
[#3327]: https://github.com/microsoft/terminal/issues/3327
|
||||
[#5772]: https://github.com/microsoft/terminal/pull/5772
|
||||
[#5000]: https://github.com/microsoft/terminal/issues/5000
|
||||
[#603]: https://github.com/microsoft/terminal/issues/603
|
||||
[#980]: https://github.com/microsoft/terminal/issues/980
|
||||
[#2529]: https://github.com/microsoft/terminal/pull/2529
|
||||
[#6062]: https://github.com/microsoft/terminal/pull/6062
|
||||
[#1410]: https://github.com/microsoft/terminal/issues/1410
|
||||
[#1000]: https://github.com/microsoft/terminal/issues/1000
|
||||
[#576]: https://github.com/microsoft/terminal/issues/576
|
||||
[#756]: https://github.com/microsoft/terminal/issues/756
|
||||
[#1060]: https://github.com/microsoft/terminal/issues/1060
|
||||
[#6100]: https://github.com/microsoft/terminal/pull/6100
|
||||
[#961]: https://github.com/microsoft/terminal/issues/961
|
||||
[#960]: https://github.com/microsoft/terminal/issues/960
|
||||
[#766]: https://github.com/microsoft/terminal/issues/766
|
||||
[#653]: https://github.com/microsoft/terminal/issues/653
|
||||
[#1553]: https://github.com/microsoft/terminal/issues/1553
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This doc will hopefully provide a useful guide for adding profiles for common
|
||||
third-party tools to your
|
||||
[profiles.json](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingJsonSettings.md)
|
||||
[settings.json](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingJsonSettings.md)
|
||||
file.
|
||||
|
||||
All of these profiles are provided _without_ their `guid` set. If you'd like to
|
||||
|
||||
@@ -33,7 +33,7 @@ Display the help message.
|
||||
|
||||
Opens a new tab with the given customizations. On its _first_ invocation, also
|
||||
opens a new window. Subsequent `new-tab` commands will all open new tabs in the
|
||||
same window.
|
||||
same window. <sup>[[1](#footnote-1)]</sup>
|
||||
|
||||
**Parameters**:
|
||||
|
||||
@@ -41,17 +41,18 @@ same window.
|
||||
|
||||
#### `split-pane`
|
||||
|
||||
`split-pane [-H]|[-V] [terminal_parameters]`
|
||||
`split-pane [-H,--horizontal|-V,--vertical] [terminal_parameters]`
|
||||
|
||||
Creates a new pane in the currently focused tab by splitting the given pane
|
||||
vertically or horizontally.
|
||||
vertically or horizontally. <sup>[[1](#footnote-1)]</sup>
|
||||
|
||||
**Parameters**:
|
||||
|
||||
* `-H`, `-V`: Used to indicate which direction to split the pane. `-V` is
|
||||
"vertically" (think `[|]`), and `-H` is "horizontally" (think `[-]`). If
|
||||
omitted, defaults to "auto", which splits the current pane in whatever the
|
||||
larger dimension is. If both `-H` and `-V` are provided, defaults to vertical.
|
||||
* `-H,--horizontal`, `-V,--vertical`: Used to indicate which direction to split
|
||||
the pane. `-V` is "vertically" (think `[|]`), and `-H` is "horizontally"
|
||||
(think `[-]`). If omitted, defaults to "auto", which splits the current pane
|
||||
in whatever the larger dimension is. If both `-H` and `-V` are provided,
|
||||
defaults to vertical.
|
||||
* `[terminal_parameters]`: See [[terminal_parameters]](#terminal_parameters).
|
||||
|
||||
#### `focus-tab`
|
||||
@@ -89,6 +90,10 @@ following:
|
||||
selected profile. If the user wants to use a `;` in this commandline, it
|
||||
should be escaped as `\;`.
|
||||
|
||||
### Notes
|
||||
|
||||
* <span id="footnote-1"></span> [1]: If you try to run a `wt` commandline while running in a Windows Terminal window, the commandline will _always_ create a new window by default. Being able to run `wt` commandlines in the _current_ window is planned in the future - for more information, refer to [#4472].
|
||||
|
||||
## Examples
|
||||
|
||||
### Open Windows Terminal in the current directory
|
||||
@@ -120,9 +125,77 @@ This creates a new Windows Terminal window with one tab, and 3 panes:
|
||||
* `split-pane -p "Windows PowerShell"`: This will create a new pane, split from
|
||||
the parent with the default profile. This pane will open with the "Windows
|
||||
PowerShell" profile
|
||||
* `split-pane -H wsl.exe`: This will create a third pane, slit _horizontally_
|
||||
* `split-pane -H wsl.exe`: This will create a third pane, split _horizontally_
|
||||
from the "Windows PowerShell" pane. It will be running the default profile,
|
||||
and will use `wsl.exe` as the commandline (instead of the default profile's
|
||||
`commandline`).
|
||||
|
||||
|
||||
### Using multiple commands from PowerShell
|
||||
|
||||
The Windows Terminal uses the semicolon character `;` as a delimiter for
|
||||
separating subcommands in the `wt` commandline. Unfortunately, `powershell` also
|
||||
uses `;` as a command separator. To work around this you can use the following
|
||||
tricks to help run multiple wt sub commands from powershell. In all the
|
||||
following examples, we'll be creating a new Terminal window with three panes -
|
||||
one running `cmd`, one with `powershell`, and a last one running `wsl`.
|
||||
|
||||
In each of the following examples, we're using the `Start-Process` command to
|
||||
run `wt`. For more information on why we're using `Start-Process`, see ["Using
|
||||
`start`"](#using-start) below.
|
||||
|
||||
#### Single quoted parameters (if you aren't calculating anything):
|
||||
|
||||
In this example, we'll wrap all the parameters to `wt` in single quotes (`'`)
|
||||
|
||||
```PowerShell
|
||||
start wt 'new-tab "cmd"; split-pane -p "Windows PowerShell" ; split-pane -H wsl.exe'
|
||||
```
|
||||
|
||||
#### Escaped quotes (if you need variables):
|
||||
|
||||
If you'd like to pass a value contained in a variable to the `wt` commandline,
|
||||
instead use the following syntax:
|
||||
|
||||
```PowerShell
|
||||
$ThirdPane = "wsl.exe"
|
||||
start wt "new-tab cmd; split-pane -p `"Windows PowerShell`" ; split-pane -H $ThirdPane"
|
||||
```
|
||||
|
||||
Note the usage of `` ` `` to escape the double-quotes (`"`) around "Windows
|
||||
Powershell" in the `-p` parameter to the `split-pane` sub-command.
|
||||
|
||||
#### Using `start`
|
||||
|
||||
All the above examples explicitly used `start` to launch the Terminal.
|
||||
|
||||
In the following examples, we're going to not use `start` to run the
|
||||
commandline. Instead, we'll try two other methods of escaping the commandline:
|
||||
* Only escaping the semicolons so that `powershell` will ignore them and pass
|
||||
them straight to `wt`.
|
||||
* Using `--%`, so powershell will treat the rest of the commandline as arguments
|
||||
to the application.
|
||||
|
||||
```PowerShell
|
||||
wt new-tab "cmd" `; split-pane -p "Windows PowerShell" `; split-pane -H wsl.exe
|
||||
```
|
||||
|
||||
```Powershell
|
||||
wt --% new-tab cmd ; split-pane -p "Windows PowerShell" ; split-pane -H wsl.exe
|
||||
```
|
||||
|
||||
In both these examples, the newly created Windows Terminal window will create
|
||||
the window by correctly parsing all the provided commandline arguments.
|
||||
|
||||
However, these methods are _not_ recommended currently, as Powershell will wait
|
||||
for the newly-created Terminal window to be closed before returning control to
|
||||
Powershell. By default, Powershell will always wait for Windows Store
|
||||
applications (like the Windows Terminal) to close before returning to the
|
||||
prompt. Note that this is different than the behavior of `cmd`, which will return
|
||||
to the prompt immediately. See
|
||||
[Powershell/PowerShell#9970](https://github.com/PowerShell/PowerShell/issues/9970)
|
||||
for more details on this bug.
|
||||
|
||||
|
||||
[#4023]: https://github.com/microsoft/terminal/pull/4023
|
||||
[#4472]: https://github.com/microsoft/terminal/issues/4472
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Editing Windows Terminal JSON Settings
|
||||
|
||||
One way (currently the only way) to configure Windows Terminal is by editing the
|
||||
`profiles.json` settings file. At the time of writing you can open the settings
|
||||
`settings.json` settings file. At the time of writing you can open the settings
|
||||
file in your default editor by selecting `Settings` from the WT pull down menu.
|
||||
|
||||
The settings are stored in the file `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\profiles.json`.
|
||||
The settings are stored in the file `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json`.
|
||||
|
||||
As of [#2515](https://github.com/microsoft/terminal/pull/2515), the settings are
|
||||
split into _two_ files: a hardcoded `defaults.json`, and `profiles.json`, which
|
||||
split into _two_ files: a hardcoded `defaults.json`, and `settings.json`, which
|
||||
contains the user settings. Users should only be concerned with the contents of
|
||||
the `profiles.json`, which contains their customizations. The `defaults.json`
|
||||
the `settings.json`, which contains their customizations. The `defaults.json`
|
||||
file is only provided as a reference of what the default settings are. For more
|
||||
details on how these two files work, see [Settings
|
||||
Layering](#settings-layering). To view the default settings file, click on the
|
||||
@@ -38,16 +38,17 @@ not affect a particular terminal instance.
|
||||
Example settings include
|
||||
|
||||
```json
|
||||
{
|
||||
"defaultProfile" : "{58ad8b0c-3ef8-5f4d-bc6f-13e4c00f2530}",
|
||||
"initialCols" : 120,
|
||||
"initialRows" : 50,
|
||||
"requestedTheme" : "system",
|
||||
"theme" : "system",
|
||||
"keybindings" : []
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
These global properties can exist either in the root json object, or in an
|
||||
object under a root property `"globals"`.
|
||||
These global properties should exist in the root json object.
|
||||
|
||||
## Key Bindings
|
||||
|
||||
@@ -87,7 +88,7 @@ This will allow you to simplify the above snippet as follows:
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
A list of default key bindings is available [here](https://github.com/microsoft/terminal/blob/master/src/cascadia/TerminalApp/defaults.json#L204).
|
||||
|
||||
### Unbinding keys
|
||||
|
||||
@@ -159,12 +160,12 @@ Example settings include
|
||||
|
||||
The profile GUID is used to reference the default profile in the global settings.
|
||||
|
||||
The values for background image stretch mode are documented [here](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.media.stretch)
|
||||
The values for background image stretch mode are documented [here](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.media.stretch).
|
||||
|
||||
### Hiding a profile
|
||||
|
||||
If you want to remove a profile from the list of profiles in the new tab
|
||||
dropdown, but keep the profile around in your `profiles.json` file, you can add
|
||||
dropdown, but keep the profile around in your `settings.json` file, you can add
|
||||
the property `"hidden": true` to the profile's json. This can also be used to
|
||||
remove the default `cmd` and PowerShell profiles, if the user does not wish to
|
||||
see them.
|
||||
@@ -198,10 +199,10 @@ The runtime settings are actually constructed from _three_ sources:
|
||||
profiles for both Windows PowerShell and Command Prompt (`cmd.exe`).
|
||||
* Dynamic Profiles, which are generated at runtime. These include Powershell
|
||||
Core, the Azure Cloud Shell connector, and profiles for and WSL distros.
|
||||
* The user settings from `profiles.json`.
|
||||
* The user settings from `settings.json`.
|
||||
|
||||
Settings from each of these sources are "layered" upon the settings from
|
||||
previous sources. In this manner, the user settings in `profiles.json` can
|
||||
previous sources. In this manner, the user settings in `settings.json` can
|
||||
contain _only the changes from the default settings_. For example, if a user
|
||||
would like to only change the color scheme of the default `cmd` profile to
|
||||
"Solarized Dark", you could change your cmd profile to the following:
|
||||
@@ -220,7 +221,7 @@ with that GUID will all be treated as the same object. Any changes in that
|
||||
profile will overwrite those from the defaults.
|
||||
|
||||
Similarly, you can overwrite settings from a color scheme by defining a color
|
||||
scheme in `profiles.json` with the same name as a default color scheme.
|
||||
scheme in `settings.json` with the same name as a default color scheme.
|
||||
|
||||
If you'd like to unbind a keystroke that's bound to an action in the default
|
||||
keybindings, you can set the `"command"` to `"unbound"` or `null`. This will
|
||||
@@ -230,9 +231,9 @@ performing the default action.
|
||||
### Dynamic Profiles
|
||||
|
||||
When dynamic profiles are created at runtime, they'll be added to the
|
||||
`profiles.json` file. You can identify these profiles by the presence of a
|
||||
`settings.json` file. You can identify these profiles by the presence of a
|
||||
`"source"` property. These profiles are tied to their source - if you uninstall
|
||||
a linux distro, then the profile will remain in your `profiles.json` file, but
|
||||
a linux distro, then the profile will remain in your `settings.json` file, but
|
||||
the profile will be hidden.
|
||||
|
||||
The Windows Terminal uses the `guid` property of these dynamically-generated
|
||||
@@ -246,11 +247,17 @@ like to hide all the WSL profiles, you could add the following setting:
|
||||
|
||||
```json
|
||||
|
||||
"disabledProfileSources": ["Microsoft.Terminal.WSL"],
|
||||
"disabledProfileSources": ["Windows.Terminal.WSL"],
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
> 👉 **NOTE**: On launch, if a dynamic profile generator is enabled, it will
|
||||
> always add new profiles it detects to your list of profiles. If you delete a
|
||||
> dynamically generated profile from your list of profiles, it will just get
|
||||
> re-added the next time the Terminal is launched! To remove a dynamic profile
|
||||
> from your list of profiles, make sure to set `"hidden": true` in the profile.
|
||||
|
||||
### Default settings
|
||||
|
||||
In [#2325](https://github.com/microsoft/terminal/issues/2325), we introduced the
|
||||
@@ -371,7 +378,7 @@ In the above settings, the `"fontFace"` in the `cmd.exe` profile overrides the
|
||||
1. Download the [Debian JPG logo](https://www.debian.org/logos/openlogo-100.jpg)
|
||||
2. Put the image in the
|
||||
`$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>\LocalState\`
|
||||
directory (same directory as your `profiles.json` file).
|
||||
directory (same directory as your `settings.json` file).
|
||||
|
||||
__NOTE__: You can put the image anywhere you like, the above suggestion happens to be convenient.
|
||||
3. Open your WT json properties file.
|
||||
@@ -392,7 +399,7 @@ Notes:
|
||||
|
||||
1. You will need to experiment with different color settings
|
||||
and schemes to make your terminal text visible on top of your image
|
||||
2. If you store the image in the UWP directory (the same directory as your profiles.json file),
|
||||
2. If you store the image in the UWP directory (the same directory as your settings.json file),
|
||||
then you should use the URI style path name given in the above example.
|
||||
More information about UWP URI schemes [here](https://docs.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes).
|
||||
3. Instead of using a UWP URI you can use a:
|
||||
@@ -414,7 +421,7 @@ following objects into your `globals.keybindings` array:
|
||||
{ "command": "paste", "keys": ["ctrl+shift+v"] }
|
||||
```
|
||||
|
||||
> 👉 **Note**: you can also add a keybinding for the `copyTextWithoutNewlines` command. This removes newlines as the text is copied to your clipboard.
|
||||
> 👉 **Note**: you can also add a keybinding for the `copy` command with the argument `"trimWhitespace": true`. This removes newlines as the text is copied to your clipboard.
|
||||
|
||||
This will add copy and paste on <kbd>ctrl+shift+c</kbd>
|
||||
and <kbd>ctrl+shift+v</kbd> respectively.
|
||||
|
||||
@@ -25,7 +25,7 @@ NOTE: The default shell is PowerShell; you can change this using the _Running a
|
||||
|
||||
### Command line options
|
||||
|
||||
None at this time. See issue [#607](https://github.com/microsoft/terminal/issues/607)
|
||||
Windows Terminal has implemented a rich set of command-line options in part as response to issue [#607](https://github.com/microsoft/terminal/issues/607). See [UsingCommandlineArguments.md](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingCommandlineArguments.md) for details.
|
||||
|
||||
## Multiple Tabs
|
||||
|
||||
@@ -45,7 +45,7 @@ To customize the shell list, see the _Configuring Windows Terminal_ section belo
|
||||
|
||||
## Starting a new PowerShell tab with admin privilege
|
||||
|
||||
There is no current plan to support this feature for security reasons. See issue [#623](https://github.com/microsoft/terminal/issues/632)
|
||||
There is no current plan to support this feature for security reasons. See issue [#632](https://github.com/microsoft/terminal/issues/632)
|
||||
|
||||
## Selecting and Copying Text in Windows Terminal
|
||||
|
||||
@@ -61,11 +61,11 @@ Copy and paste operations can also be keybound. For more information on how to b
|
||||
|
||||
## Add a "Open Windows Terminal Here" to File Explorer
|
||||
|
||||
Not currently supported "out of the box". See issue [#1060](https://github.com/microsoft/terminal/issues/1060)
|
||||
Not currently supported "out of the box" (See issue [#1060](https://github.com/microsoft/terminal/issues/1060)). However, you can open Windows Terminal in current directory by typing `wt -d .` in the Explorer address bar.
|
||||
|
||||
## Configuring Windows Terminal
|
||||
|
||||
All Windows Terminal settings are currently managed using the `profiles.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState`.
|
||||
All Windows Terminal settings are currently managed using the `settings.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState`.
|
||||
|
||||
To open the settings file from Windows Terminal:
|
||||
|
||||
@@ -73,7 +73,7 @@ To open the settings file from Windows Terminal:
|
||||
2. From the dropdown list, click `Settings`. You can also use a shortcut: <kbd>Ctrl</kbd>+<kbd>,</kbd>.
|
||||
3. Your default `json` editor will open the settings file.
|
||||
|
||||
For an introduction to the various settings, see [Using Json Settings](UsingJsonSettings.md). The list of valid settings can be found in the [profiles.json documentation](../cascadia/SettingsSchema.md) section.
|
||||
For an introduction to the various settings, see [Using Json Settings](UsingJsonSettings.md). The list of valid settings can be found in the [settings.json documentation](../cascadia/SettingsSchema.md) section.
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
@@ -87,4 +87,5 @@ For an introduction to the various settings, see [Using Json Settings](UsingJson
|
||||
2. Terminal zoom can be changed by holding <kbd>Ctrl</kbd> and scrolling with mouse.
|
||||
3. Background opacity can be changed by holding <kbd>Ctrl</kbd>+<kbd>Shift</kbd> and scrolling with mouse. Note that acrylic transparency is limited by the OS only to focused windows.
|
||||
4. Open Windows Terminal in current directory by typing `wt -d .` in the address bar.
|
||||
5. Please add more Tips and Tricks.
|
||||
5. Pin the Windows Terminal to the taskbar. Now it can be launched using the Windows shortcut <kbd>Win</kbd>+<kbd>Number</kbd> (e.g. <kbd>Win</kbd>+<kbd>1</kbd> or any other number based on the position in the taskbar!). Press <kbd>Win</kbd>+<kbd>Shift</kbd>+<kbd>Number</kbd> to always launch a new window.
|
||||
6. Please add more Tips and Tricks.
|
||||
|
||||
8
oss/README.md
Normal file
8
oss/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
### Component-Governance-tracked OSS dependencies
|
||||
|
||||
This directory contains mirrored open-source projects that are used by the
|
||||
console host and Windows Terminal. Code in this directory will be replicated
|
||||
into the Windows OS repository.
|
||||
|
||||
All projects in this directory **must** bear Component Governance Manifests
|
||||
(`cgmanifest.json` files) indicating their provenance.
|
||||
27
oss/fmt/LICENSE.rst
Normal file
27
oss/fmt/LICENSE.rst
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 - present, Victor Zverovich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
17
oss/fmt/MAINTAINER_README.md
Normal file
17
oss/fmt/MAINTAINER_README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
### Notes for Future Maintainers
|
||||
|
||||
This was originally imported by @DHowett-MSFT in April 2020.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
1. Go to fmtlib/fmt repository on GitHub.
|
||||
2. Take the entire contents of the include/ and src/ directories and drop them in this directory.
|
||||
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.
|
||||
If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage.
|
||||
5. Submit the pull.
|
||||
|
||||
13
oss/fmt/cgmanifest.json
Normal file
13
oss/fmt/cgmanifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{"Registrations":[
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/fmtlib/fmt",
|
||||
"commitHash": "9bdd1596cef1b57b9556f8bef32dc4a32322ef3e"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
1119
oss/fmt/include/fmt/chrono.h
Normal file
1119
oss/fmt/include/fmt/chrono.h
Normal file
File diff suppressed because it is too large
Load Diff
568
oss/fmt/include/fmt/color.h
Normal file
568
oss/fmt/include/fmt/color.h
Normal file
@@ -0,0 +1,568 @@
|
||||
// Formatting library for C++ - color support
|
||||
//
|
||||
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COLOR_H_
|
||||
#define FMT_COLOR_H_
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
enum class color : uint32_t {
|
||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||
black = 0x000000, // rgb(0,0,0)
|
||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||
blue = 0x0000FF, // rgb(0,0,255)
|
||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||
brown = 0xA52A2A, // rgb(165,42,42)
|
||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||
coral = 0xFF7F50, // rgb(255,127,80)
|
||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||
crimson = 0xDC143C, // rgb(220,20,60)
|
||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||
dark_green = 0x006400, // rgb(0,100,0)
|
||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||
dim_gray = 0x696969, // rgb(105,105,105)
|
||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||
forest_green = 0x228B22, // rgb(34,139,34)
|
||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||
gold = 0xFFD700, // rgb(255,215,0)
|
||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||
gray = 0x808080, // rgb(128,128,128)
|
||||
green = 0x008000, // rgb(0,128,0)
|
||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||
indigo = 0x4B0082, // rgb(75,0,130)
|
||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||
light_coral = 0xF08080, // rgb(240,128,128)
|
||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||
light_green = 0x90EE90, // rgb(144,238,144)
|
||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||
lime = 0x00FF00, // rgb(0,255,0)
|
||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||
maroon = 0x800000, // rgb(128,0,0)
|
||||
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||
navy = 0x000080, // rgb(0,0,128)
|
||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||
olive = 0x808000, // rgb(128,128,0)
|
||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||
orange = 0xFFA500, // rgb(255,165,0)
|
||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||
peru = 0xCD853F, // rgb(205,133,63)
|
||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||
purple = 0x800080, // rgb(128,0,128)
|
||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||
red = 0xFF0000, // rgb(255,0,0)
|
||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||
salmon = 0xFA8072, // rgb(250,128,114)
|
||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||
sienna = 0xA0522D, // rgb(160,82,45)
|
||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||
slate_gray = 0x708090, // rgb(112,128,144)
|
||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||
tan = 0xD2B48C, // rgb(210,180,140)
|
||||
teal = 0x008080, // rgb(0,128,128)
|
||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||
tomato = 0xFF6347, // rgb(255,99,71)
|
||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||
violet = 0xEE82EE, // rgb(238,130,238)
|
||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||
white = 0xFFFFFF, // rgb(255,255,255)
|
||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||
}; // enum class color
|
||||
|
||||
enum class terminal_color : uint8_t {
|
||||
black = 30,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
blue,
|
||||
magenta,
|
||||
cyan,
|
||||
white,
|
||||
bright_black = 90,
|
||||
bright_red,
|
||||
bright_green,
|
||||
bright_yellow,
|
||||
bright_blue,
|
||||
bright_magenta,
|
||||
bright_cyan,
|
||||
bright_white
|
||||
};
|
||||
|
||||
enum class emphasis : uint8_t {
|
||||
bold = 1,
|
||||
italic = 1 << 1,
|
||||
underline = 1 << 2,
|
||||
strikethrough = 1 << 3
|
||||
};
|
||||
|
||||
// rgb is a struct for red, green and blue colors.
|
||||
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||
struct rgb {
|
||||
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
||||
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||
FMT_CONSTEXPR rgb(uint32_t hex)
|
||||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||
FMT_CONSTEXPR rgb(color hex)
|
||||
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||
g((uint32_t(hex) >> 8) & 0xFF),
|
||||
b(uint32_t(hex) & 0xFF) {}
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
// color is a struct of either a rgb color or a terminal color.
|
||||
struct color_type {
|
||||
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
|
||||
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
|
||||
value{} {
|
||||
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||
}
|
||||
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
|
||||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||
}
|
||||
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
|
||||
value{} {
|
||||
value.term_color = static_cast<uint8_t>(term_color);
|
||||
}
|
||||
bool is_rgb;
|
||||
union color_union {
|
||||
uint8_t term_color;
|
||||
uint32_t rgb_color;
|
||||
} value;
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
// Experimental text formatting support.
|
||||
class text_style {
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems(em) {}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
if (!set_background_color) {
|
||||
set_background_color = rhs.set_background_color;
|
||||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||
static_cast<uint8_t>(rhs.ems));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
if (!set_background_color) {
|
||||
set_background_color = rhs.set_background_color;
|
||||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||
static_cast<uint8_t>(rhs.ems));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs &= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||
return set_foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||
return set_background_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||
return static_cast<uint8_t>(ems) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||
return foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||
return background_color;
|
||||
}
|
||||
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||
return ems;
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
internal::color_type text_color) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems() {
|
||||
if (is_foreground) {
|
||||
foreground_color = text_color;
|
||||
set_foreground_color = true;
|
||||
} else {
|
||||
background_color = text_color;
|
||||
set_background_color = true;
|
||||
}
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
||||
FMT_NOEXCEPT;
|
||||
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
internal::color_type foreground_color;
|
||||
internal::color_type background_color;
|
||||
bool set_foreground_color;
|
||||
bool set_background_color;
|
||||
emphasis ems;
|
||||
};
|
||||
|
||||
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
||||
return text_style(/*is_foreground=*/true, foreground);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
||||
return text_style(/*is_foreground=*/false, background);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
||||
return text_style(lhs) | rhs;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
||||
const char* esc) FMT_NOEXCEPT {
|
||||
// If we have a terminal color, we need to output another escape code
|
||||
// sequence.
|
||||
if (!text_color.is_rgb) {
|
||||
bool is_background = esc == internal::data::background_color;
|
||||
uint32_t value = text_color.value.term_color;
|
||||
// Background ASCII codes are the same as the foreground ones but with
|
||||
// 10 more.
|
||||
if (is_background) value += 10u;
|
||||
|
||||
std::size_t index = 0;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
|
||||
if (value >= 100u) {
|
||||
buffer[index++] = static_cast<Char>('1');
|
||||
value %= 100u;
|
||||
}
|
||||
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
buffer[index++] = static_cast<Char>('\0');
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
buffer[i] = static_cast<Char>(esc[i]);
|
||||
}
|
||||
rgb color(text_color.value.rgb_color);
|
||||
to_esc(color.r, buffer + 7, ';');
|
||||
to_esc(color.g, buffer + 11, ';');
|
||||
to_esc(color.b, buffer + 15, 'm');
|
||||
buffer[19] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||
uint8_t em_codes[4] = {};
|
||||
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||
em_codes[3] = 9;
|
||||
|
||||
std::size_t index = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (!em_codes[i]) continue;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
}
|
||||
buffer[index++] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
||||
|
||||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
||||
return buffer + std::char_traits<Char>::length(buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
Char buffer[7u + 3u * 4u + 1u];
|
||||
|
||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||
char delimiter) FMT_NOEXCEPT {
|
||||
out[0] = static_cast<Char>('0' + c / 100);
|
||||
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||
out[2] = static_cast<Char>('0' + c % 10);
|
||||
out[3] = static_cast<Char>(delimiter);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||
internal::color_type foreground) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||
internal::color_type background) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(background, internal::data::background_color);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(em);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputs(chars, stream);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputws(chars, stream);
|
||||
}
|
||||
|
||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs(internal::data::reset_color, stream);
|
||||
}
|
||||
|
||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs(internal::data::wreset_color, stream);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||
const char* begin = data::reset_color;
|
||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||
buffer.append(begin, end);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
has_style = true;
|
||||
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
|
||||
buf.append(emphasis.begin(), emphasis.end());
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
auto foreground =
|
||||
internal::make_foreground_color<Char>(ts.get_foreground());
|
||||
buf.append(foreground.begin(), foreground.end());
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
auto background =
|
||||
internal::make_background_color<Char>(ts.get_background());
|
||||
buf.append(background.begin(), background.end());
|
||||
}
|
||||
internal::vformat_to(buf, format_str, args);
|
||||
if (has_style) internal::reset_color<Char>(buf);
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
internal::vformat_to(buf, ts, to_string_view(format), args);
|
||||
buf.push_back(Char(0));
|
||||
internal::fputs(buf.data(), f);
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a string and prints it to the specified file stream using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
Example:
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
internal::check_format_string<Args...>(format_str);
|
||||
using context = buffer_context<char_t<S>>;
|
||||
format_arg_store<context, Args...> as{args...};
|
||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||
specify text formatting.
|
||||
Example:
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||
return print(stdout, ts, format_str, args...);
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vformat(
|
||||
const text_style& ts, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
internal::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||
return fmt::to_string(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
#include <fmt/color.h>
|
||||
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"The answer is {}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
return vformat(ts, to_string_view(format_str),
|
||||
internal::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COLOR_H_
|
||||
595
oss/fmt/include/fmt/compile.h
Normal file
595
oss/fmt/include/fmt/compile.h
Normal file
@@ -0,0 +1,595 @@
|
||||
// Formatting library for C++ - experimental format string compilation
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COMPILE_H_
|
||||
#define FMT_COMPILE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
|
||||
// Part of a compiled format string. It can be either literal text or a
|
||||
// replacement field.
|
||||
template <typename Char> struct format_part {
|
||||
enum class kind { arg_index, arg_name, text, replacement };
|
||||
|
||||
struct replacement {
|
||||
arg_ref<Char> arg_id;
|
||||
dynamic_format_specs<Char> specs;
|
||||
};
|
||||
|
||||
kind part_kind;
|
||||
union value {
|
||||
int arg_index;
|
||||
basic_string_view<Char> str;
|
||||
replacement repl;
|
||||
|
||||
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
|
||||
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
|
||||
FMT_CONSTEXPR value(replacement r) : repl(r) {}
|
||||
} val;
|
||||
// Position past the end of the argument id.
|
||||
const Char* arg_id_end = nullptr;
|
||||
|
||||
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
|
||||
: part_kind(k), val(v) {}
|
||||
|
||||
static FMT_CONSTEXPR format_part make_arg_index(int index) {
|
||||
return format_part(kind::arg_index, index);
|
||||
}
|
||||
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
|
||||
return format_part(kind::arg_name, name);
|
||||
}
|
||||
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
|
||||
return format_part(kind::text, text);
|
||||
}
|
||||
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
|
||||
return format_part(kind::replacement, repl);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct part_counter {
|
||||
unsigned num_parts = 0;
|
||||
|
||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
||||
if (begin != end) ++num_parts;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
|
||||
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
|
||||
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
|
||||
|
||||
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
|
||||
|
||||
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
||||
const Char* end) {
|
||||
// Find the matching brace.
|
||||
unsigned brace_counter = 0;
|
||||
for (; begin != end; ++begin) {
|
||||
if (*begin == '{') {
|
||||
++brace_counter;
|
||||
} else if (*begin == '}') {
|
||||
if (brace_counter == 0u) break;
|
||||
--brace_counter;
|
||||
}
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char*) {}
|
||||
};
|
||||
|
||||
// Counts the number of parts in a format string.
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
|
||||
part_counter<Char> counter;
|
||||
parse_format_string<true>(format_str, counter);
|
||||
return counter.num_parts;
|
||||
}
|
||||
|
||||
template <typename Char, typename PartHandler>
|
||||
class format_string_compiler : public error_handler {
|
||||
private:
|
||||
using part = format_part<Char>;
|
||||
|
||||
PartHandler handler_;
|
||||
part part_;
|
||||
basic_string_view<Char> format_str_;
|
||||
basic_format_parse_context<Char> parse_context_;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
|
||||
PartHandler handler)
|
||||
: handler_(handler),
|
||||
format_str_(format_str),
|
||||
parse_context_(format_str) {}
|
||||
|
||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
||||
if (begin != end)
|
||||
handler_(part::make_text({begin, to_unsigned(end - begin)}));
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_arg_id() {
|
||||
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_arg_id(int id) {
|
||||
parse_context_.check_arg_id(id);
|
||||
part_ = part::make_arg_index(id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
|
||||
part_ = part::make_arg_name(id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
|
||||
part_.arg_id_end = ptr;
|
||||
handler_(part_);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
||||
const Char* end) {
|
||||
auto repl = typename part::replacement();
|
||||
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
|
||||
repl.specs, parse_context_);
|
||||
auto it = parse_format_specs(begin, end, handler);
|
||||
if (*it != '}') on_error("missing '}' in format string");
|
||||
repl.arg_id = part_.part_kind == part::kind::arg_index
|
||||
? arg_ref<Char>(part_.val.arg_index)
|
||||
: arg_ref<Char>(part_.val.str);
|
||||
auto part = part::make_replacement(repl);
|
||||
part.arg_id_end = begin;
|
||||
handler_(part);
|
||||
return it;
|
||||
}
|
||||
};
|
||||
|
||||
// Compiles a format string and invokes handler(part) for each parsed part.
|
||||
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
|
||||
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
|
||||
PartHandler handler) {
|
||||
parse_format_string<IS_CONSTEXPR>(
|
||||
format_str,
|
||||
format_string_compiler<Char, PartHandler>(format_str, handler));
|
||||
}
|
||||
|
||||
template <typename Range, typename Context, typename Id>
|
||||
void format_arg(
|
||||
basic_format_parse_context<typename Range::value_type>& parse_ctx,
|
||||
Context& ctx, Id arg_id) {
|
||||
ctx.advance_to(
|
||||
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
|
||||
}
|
||||
|
||||
// vformat_to is defined in a subnamespace to prevent ADL.
|
||||
namespace cf {
|
||||
template <typename Context, typename Range, typename CompiledFormat>
|
||||
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
||||
-> typename Context::iterator {
|
||||
using char_type = typename Context::char_type;
|
||||
basic_format_parse_context<char_type> parse_ctx(
|
||||
to_string_view(cf.format_str_));
|
||||
Context ctx(out.begin(), args);
|
||||
|
||||
const auto& parts = cf.parts();
|
||||
for (auto part_it = std::begin(parts); part_it != std::end(parts);
|
||||
++part_it) {
|
||||
const auto& part = *part_it;
|
||||
const auto& value = part.val;
|
||||
|
||||
using format_part_t = format_part<char_type>;
|
||||
switch (part.part_kind) {
|
||||
case format_part_t::kind::text: {
|
||||
const auto text = value.str;
|
||||
auto output = ctx.out();
|
||||
auto&& it = reserve(output, text.size());
|
||||
it = std::copy_n(text.begin(), text.size(), it);
|
||||
ctx.advance_to(output);
|
||||
break;
|
||||
}
|
||||
|
||||
case format_part_t::kind::arg_index:
|
||||
advance_to(parse_ctx, part.arg_id_end);
|
||||
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
|
||||
break;
|
||||
|
||||
case format_part_t::kind::arg_name:
|
||||
advance_to(parse_ctx, part.arg_id_end);
|
||||
internal::format_arg<Range>(parse_ctx, ctx, value.str);
|
||||
break;
|
||||
|
||||
case format_part_t::kind::replacement: {
|
||||
const auto& arg_id_value = value.repl.arg_id.val;
|
||||
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
|
||||
? ctx.arg(arg_id_value.index)
|
||||
: ctx.arg(arg_id_value.name);
|
||||
|
||||
auto specs = value.repl.specs;
|
||||
|
||||
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
|
||||
handle_dynamic_spec<precision_checker>(specs.precision,
|
||||
specs.precision_ref, ctx);
|
||||
|
||||
error_handler h;
|
||||
numeric_specs_checker<error_handler> checker(h, arg.type());
|
||||
if (specs.align == align::numeric) checker.require_numeric_argument();
|
||||
if (specs.sign != sign::none) checker.check_sign();
|
||||
if (specs.alt) checker.require_numeric_argument();
|
||||
if (specs.precision >= 0) checker.check_precision();
|
||||
|
||||
advance_to(parse_ctx, part.arg_id_end);
|
||||
ctx.advance_to(
|
||||
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ctx.out();
|
||||
}
|
||||
} // namespace cf
|
||||
|
||||
struct basic_compiled_format {};
|
||||
|
||||
template <typename S, typename = void>
|
||||
struct compiled_format_base : basic_compiled_format {
|
||||
using char_type = char_t<S>;
|
||||
using parts_container = std::vector<internal::format_part<char_type>>;
|
||||
|
||||
parts_container compiled_parts;
|
||||
|
||||
explicit compiled_format_base(basic_string_view<char_type> format_str) {
|
||||
compile_format_string<false>(format_str,
|
||||
[this](const format_part<char_type>& part) {
|
||||
compiled_parts.push_back(part);
|
||||
});
|
||||
}
|
||||
|
||||
const parts_container& parts() const { return compiled_parts; }
|
||||
};
|
||||
|
||||
template <typename Char, unsigned N> struct format_part_array {
|
||||
format_part<Char> data[N] = {};
|
||||
FMT_CONSTEXPR format_part_array() = default;
|
||||
};
|
||||
|
||||
template <typename Char, unsigned N>
|
||||
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
|
||||
basic_string_view<Char> format_str) {
|
||||
format_part_array<Char, N> parts;
|
||||
unsigned counter = 0;
|
||||
// This is not a lambda for compatibility with older compilers.
|
||||
struct {
|
||||
format_part<Char>* parts;
|
||||
unsigned* counter;
|
||||
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
|
||||
parts[(*counter)++] = part;
|
||||
}
|
||||
} collector{parts.data, &counter};
|
||||
compile_format_string<true>(format_str, collector);
|
||||
if (counter < N) {
|
||||
parts.data[counter] =
|
||||
format_part<Char>::make_text(basic_string_view<Char>());
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
|
||||
return (a < b) ? b : a;
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
|
||||
: basic_compiled_format {
|
||||
using char_type = char_t<S>;
|
||||
|
||||
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
||||
|
||||
// Workaround for old compilers. Format string compilation will not be
|
||||
// performed there anyway.
|
||||
#if FMT_USE_CONSTEXPR
|
||||
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
|
||||
constexpr_max(count_parts(to_string_view(S())), 1u);
|
||||
#else
|
||||
static const unsigned num_format_parts = 1;
|
||||
#endif
|
||||
|
||||
using parts_container = format_part<char_type>[num_format_parts];
|
||||
|
||||
const parts_container& parts() const {
|
||||
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
||||
compile_to_parts<char_type, num_format_parts>(
|
||||
internal::to_string_view(S()));
|
||||
return compiled_parts.data;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename S, typename... Args>
|
||||
class compiled_format : private compiled_format_base<S> {
|
||||
public:
|
||||
using typename compiled_format_base<S>::char_type;
|
||||
|
||||
private:
|
||||
basic_string_view<char_type> format_str_;
|
||||
|
||||
template <typename Context, typename Range, typename CompiledFormat>
|
||||
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
|
||||
basic_format_args<Context> args) ->
|
||||
typename Context::iterator;
|
||||
|
||||
public:
|
||||
compiled_format() = delete;
|
||||
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
|
||||
: compiled_format_base<S>(format_str), format_str_(format_str) {}
|
||||
};
|
||||
|
||||
#ifdef __cpp_if_constexpr
|
||||
template <typename... Args> struct type_list {};
|
||||
|
||||
// Returns a reference to the argument at index N from [first, rest...].
|
||||
template <int N, typename T, typename... Args>
|
||||
constexpr const auto& get(const T& first, const Args&... rest) {
|
||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||
if constexpr (N == 0)
|
||||
return first;
|
||||
else
|
||||
return get<N - 1>(rest...);
|
||||
}
|
||||
|
||||
template <int N, typename> struct get_type_impl;
|
||||
|
||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
|
||||
};
|
||||
|
||||
template <int N, typename T>
|
||||
using get_type = typename get_type_impl<N, T>::type;
|
||||
|
||||
template <typename T> struct is_compiled_format : std::false_type {};
|
||||
|
||||
template <typename Char> struct text {
|
||||
basic_string_view<Char> data;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&...) const {
|
||||
// TODO: reserve
|
||||
return copy_str<Char>(data.begin(), data.end(), out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
||||
size_t size) {
|
||||
return {{&s[pos], size}};
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
std::enable_if_t<std::is_integral_v<T>, int> = 0>
|
||||
OutputIt format_default(OutputIt out, T value) {
|
||||
// TODO: reserve
|
||||
format_int fi(value);
|
||||
return std::copy(fi.data(), fi.data() + fi.size(), out);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
OutputIt format_default(OutputIt out, double value) {
|
||||
writer w(out);
|
||||
w.write(value);
|
||||
return w.out();
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
OutputIt format_default(OutputIt out, Char value) {
|
||||
*out++ = value;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
OutputIt format_default(OutputIt out, const Char* value) {
|
||||
auto length = std::char_traits<Char>::length(value);
|
||||
return copy_str<Char>(value, value + length, out);
|
||||
}
|
||||
|
||||
// A replacement field that refers to argument N.
|
||||
template <typename Char, typename T, int N> struct field {
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&... args) const {
|
||||
// This ensures that the argument type is convertile to `const T&`.
|
||||
const T& arg = get<N>(args...);
|
||||
return format_default<Char>(out, arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R> struct concat {
|
||||
L lhs;
|
||||
R rhs;
|
||||
using char_type = typename L::char_type;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&... args) const {
|
||||
out = lhs.format(out, args...);
|
||||
return rhs.format(out, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
||||
return {lhs, rhs};
|
||||
}
|
||||
|
||||
struct unknown_format {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
||||
for (size_t size = str.size(); pos != size; ++pos) {
|
||||
if (str[pos] == '{' || str[pos] == '}') break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str);
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||
constexpr auto parse_tail(T head, S format_str) {
|
||||
if constexpr (POS != to_string_view(format_str).size()) {
|
||||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
||||
unknown_format>())
|
||||
return tail;
|
||||
else
|
||||
return make_concat(head, tail);
|
||||
} else {
|
||||
return head;
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles a non-empty format string and returns the compiled representation
|
||||
// or unknown_format() on unrecognized input.
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr basic_string_view<char_type> str = format_str;
|
||||
if constexpr (str[POS] == '{') {
|
||||
if (POS + 1 == str.size())
|
||||
throw format_error("unmatched '{' in format string");
|
||||
if constexpr (str[POS + 1] == '{') {
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else if constexpr (str[POS + 1] == '}') {
|
||||
using type = get_type<ID, Args>;
|
||||
if constexpr (std::is_same<type, int>::value) {
|
||||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
||||
format_str);
|
||||
} else {
|
||||
return unknown_format();
|
||||
}
|
||||
} else {
|
||||
return unknown_format();
|
||||
}
|
||||
} else if constexpr (str[POS] == '}') {
|
||||
if (POS + 1 == str.size())
|
||||
throw format_error("unmatched '}' in format string");
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else {
|
||||
constexpr auto end = parse_text(str, POS + 1);
|
||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||
format_str);
|
||||
}
|
||||
}
|
||||
#endif // __cpp_if_constexpr
|
||||
} // namespace internal
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
# ifdef __cpp_if_constexpr
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||
constexpr auto compile(S format_str) {
|
||||
constexpr basic_string_view<typename S::char_type> str = format_str;
|
||||
if constexpr (str.size() == 0) {
|
||||
return internal::make_text(str, 0, 0);
|
||||
} else {
|
||||
constexpr auto result =
|
||||
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
|
||||
format_str);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
||||
internal::unknown_format>()) {
|
||||
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
cf.format(std::back_inserter(buffer), args...);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
return cf.format(out, args...);
|
||||
}
|
||||
# else
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
|
||||
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
||||
}
|
||||
# endif // __cpp_if_constexpr
|
||||
#endif // FMT_USE_CONSTEXPR
|
||||
|
||||
// Compiles the format string which must be a string literal.
|
||||
template <typename... Args, typename Char, size_t N>
|
||||
auto compile(const Char (&format_str)[N])
|
||||
-> internal::compiled_format<const Char*, Args...> {
|
||||
return internal::compiled_format<const Char*, Args...>(
|
||||
basic_string_view<Char>(format_str, N - 1));
|
||||
}
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
||||
CompiledFormat>::value)>
|
||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
using range = buffer_range<Char>;
|
||||
using context = buffer_context<Char>;
|
||||
internal::cf::vformat_to<context>(range(buffer), cf,
|
||||
make_format_args<context>(args...));
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
||||
CompiledFormat>::value)>
|
||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
using char_type = typename CompiledFormat::char_type;
|
||||
using range = internal::output_range<OutputIt, char_type>;
|
||||
using context = format_context_t<OutputIt, char_type>;
|
||||
return internal::cf::vformat_to<context>(range(out), cf,
|
||||
make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||
const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
auto it =
|
||||
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||
return {it.base(), it.count()};
|
||||
}
|
||||
|
||||
template <typename CompiledFormat, typename... Args>
|
||||
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||
return format_to(internal::counting_iterator(), cf, args...).count();
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COMPILE_H_
|
||||
1789
oss/fmt/include/fmt/core.h
Normal file
1789
oss/fmt/include/fmt/core.h
Normal file
File diff suppressed because it is too large
Load Diff
1403
oss/fmt/include/fmt/format-inl.h
Normal file
1403
oss/fmt/include/fmt/format-inl.h
Normal file
File diff suppressed because it is too large
Load Diff
3648
oss/fmt/include/fmt/format.h
Normal file
3648
oss/fmt/include/fmt/format.h
Normal file
File diff suppressed because it is too large
Load Diff
78
oss/fmt/include/fmt/locale.h
Normal file
78
oss/fmt/include/fmt/locale.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// Formatting library for C++ - std::locale support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_LOCALE_H_
|
||||
#define FMT_LOCALE_H_
|
||||
|
||||
#include <locale>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace internal {
|
||||
template <typename Char>
|
||||
typename buffer_context<Char>::iterator vformat_to(
|
||||
const std::locale& loc, buffer<Char>& buf,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
using range = buffer_range<Char>;
|
||||
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
||||
internal::locale_ref(loc));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
std::basic_string<Char> vformat(
|
||||
const std::locale& loc, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::vformat_to(loc, buffer, format_str, args);
|
||||
return fmt::to_string(buffer);
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vformat(
|
||||
const std::locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
return internal::vformat(loc, to_string_view(format_str), args);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
return internal::vformat(
|
||||
loc, to_string_view(format_str),
|
||||
internal::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
template <typename S, typename OutputIt, typename... Args,
|
||||
typename Char = enable_if_t<
|
||||
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||
inline OutputIt vformat_to(
|
||||
OutputIt out, const std::locale& loc, const S& format_str,
|
||||
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
||||
using range = internal::output_range<OutputIt, Char>;
|
||||
return vformat_to<arg_formatter<range>>(
|
||||
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
|
||||
internal::is_string<S>::value)>
|
||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
internal::check_format_string<Args...>(format_str);
|
||||
using context = format_context_t<OutputIt, char_t<S>>;
|
||||
format_arg_store<context, Args...> as{args...};
|
||||
return vformat_to(out, loc, to_string_view(format_str),
|
||||
basic_format_args<context>(as));
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_LOCALE_H_
|
||||
392
oss/fmt/include/fmt/os.h
Normal file
392
oss/fmt/include/fmt/os.h
Normal file
@@ -0,0 +1,392 @@
|
||||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OS_H_
|
||||
#define FMT_OS_H_
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||
# undef __STRICT_ANSI__
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <clocale> // for locale_t
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // for strtod_l
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
// UWP doesn't provide _pipe.
|
||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
#endif
|
||||
#if FMT_HAS_INCLUDE("fcntl.h") && \
|
||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
#else
|
||||
# define FMT_USE_FCNTL 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||
#ifdef FMT_SYSTEM
|
||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) call
|
||||
# ifdef _WIN32
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX_CALL(call) ::_##call
|
||||
# else
|
||||
# define FMT_POSIX_CALL(call) ::call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Retries the expression while it evaluates to error_result and errno
|
||||
// equals to EINTR.
|
||||
#ifndef _WIN32
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||
do { \
|
||||
(result) = (expression); \
|
||||
} while ((result) == (error_result) && errno == EINTR)
|
||||
#else
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
/**
|
||||
\rst
|
||||
A reference to a null-terminated string. It can be constructed from a C
|
||||
string or ``std::string``.
|
||||
|
||||
You can use one of the following type aliases for common character types:
|
||||
|
||||
+---------------+-----------------------------+
|
||||
| Type | Definition |
|
||||
+===============+=============================+
|
||||
| cstring_view | basic_cstring_view<char> |
|
||||
+---------------+-----------------------------+
|
||||
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||
+---------------+-----------------------------+
|
||||
|
||||
This class is most useful as a parameter type to allow passing
|
||||
different types of strings to a function, for example::
|
||||
|
||||
template <typename... Args>
|
||||
std::string format(cstring_view format_str, const Args & ... args);
|
||||
|
||||
format("{}", 42);
|
||||
format(std::string("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char> class basic_cstring_view {
|
||||
private:
|
||||
const Char* data_;
|
||||
|
||||
public:
|
||||
/** Constructs a string reference object from a C string. */
|
||||
basic_cstring_view(const Char* s) : data_(s) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
\endrst
|
||||
*/
|
||||
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
||||
|
||||
/** Returns the pointer to a C string. */
|
||||
const Char* c_str() const { return data_; }
|
||||
};
|
||||
|
||||
using cstring_view = basic_cstring_view<char>;
|
||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||
|
||||
// An error code.
|
||||
class error_code {
|
||||
private:
|
||||
int value_;
|
||||
|
||||
public:
|
||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||
|
||||
int get() const FMT_NOEXCEPT { return value_; }
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace internal {
|
||||
// A converter from UTF-16 to UTF-8.
|
||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||
class utf16_to_utf8 {
|
||||
private:
|
||||
memory_buffer buffer_;
|
||||
|
||||
public:
|
||||
utf16_to_utf8() {}
|
||||
FMT_API explicit utf16_to_utf8(wstring_view s);
|
||||
operator string_view() const { return string_view(&buffer_[0], size()); }
|
||||
size_t size() const { return buffer_.size() - 1; }
|
||||
const char* c_str() const { return &buffer_[0]; }
|
||||
std::string str() const { return std::string(&buffer_[0], size()); }
|
||||
|
||||
// Performs conversion returning a system error code instead of
|
||||
// throwing exception on conversion error. This method may still throw
|
||||
// in case of memory allocation error.
|
||||
FMT_API int convert(wstring_view s);
|
||||
};
|
||||
|
||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||
string_view message) FMT_NOEXCEPT;
|
||||
} // namespace internal
|
||||
|
||||
/** A Windows error. */
|
||||
class windows_error : public system_error {
|
||||
private:
|
||||
FMT_API void init(int error_code, string_view format_str, format_args args);
|
||||
|
||||
public:
|
||||
/**
|
||||
\rst
|
||||
Constructs a :class:`fmt::windows_error` object with the description
|
||||
of the form
|
||||
|
||||
.. parsed-literal::
|
||||
*<message>*: *<system-message>*
|
||||
|
||||
where *<message>* is the formatted message and *<system-message>* is the
|
||||
system message corresponding to the error code.
|
||||
*error_code* is a Windows error code as given by ``GetLastError``.
|
||||
If *error_code* is not a valid error code such as -1, the system message
|
||||
will look like "error -1".
|
||||
|
||||
**Example**::
|
||||
|
||||
// This throws a windows_error with the description
|
||||
// cannot open file 'madeup': The system cannot find the file specified.
|
||||
// or similar (system message may vary).
|
||||
const char *filename = "madeup";
|
||||
LPOFSTRUCT of = LPOFSTRUCT();
|
||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
if (file == HFILE_ERROR) {
|
||||
throw fmt::windows_error(GetLastError(),
|
||||
"cannot open file '{}'", filename);
|
||||
}
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
windows_error(int error_code, string_view message, const Args&... args) {
|
||||
init(error_code, message, make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_windows_error(int error_code,
|
||||
string_view message) FMT_NOEXCEPT;
|
||||
#endif // _WIN32
|
||||
|
||||
// A buffered file.
|
||||
class buffered_file {
|
||||
private:
|
||||
FILE* file_;
|
||||
|
||||
friend class file;
|
||||
|
||||
explicit buffered_file(FILE* f) : file_(f) {}
|
||||
|
||||
public:
|
||||
buffered_file(const buffered_file&) = delete;
|
||||
void operator=(const buffered_file&) = delete;
|
||||
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
|
||||
buffered_file& operator=(buffered_file&& other) {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
FILE* get() const FMT_NOEXCEPT { return file_; }
|
||||
|
||||
// We place parentheses around fileno to workaround a bug in some versions
|
||||
// of MinGW that define fileno as a macro.
|
||||
FMT_API int(fileno)() const;
|
||||
|
||||
void vprint(string_view format_str, format_args args) {
|
||||
fmt::vprint(file_, format_str, args);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void print(string_view format_str, const Args&... args) {
|
||||
vprint(format_str, make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
// A file. Closed file is represented by a file object with descriptor -1.
|
||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||
// fmt::system_error in case of failure. Note that some errors such as
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class file {
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a file object with a given descriptor.
|
||||
explicit file(int fd) : fd_(fd) {}
|
||||
|
||||
public:
|
||||
// Possible values for the oflag argument to the constructor.
|
||||
enum {
|
||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||
};
|
||||
|
||||
// Constructs a file object which doesn't represent any file.
|
||||
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
FMT_API file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(const file&) = delete;
|
||||
void operator=(const file&) = delete;
|
||||
|
||||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
||||
|
||||
file& operator=(file&& other) FMT_NOEXCEPT {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~file() FMT_NOEXCEPT;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
FMT_API long long size() const;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
FMT_API std::size_t read(void* buffer, std::size_t count);
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
FMT_API std::size_t write(const void* buffer, std::size_t count);
|
||||
|
||||
// Duplicates a file descriptor with the dup function and returns
|
||||
// the duplicate as a file object.
|
||||
FMT_API static file dup(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
FMT_API static void pipe(file& read_end, file& write_end);
|
||||
|
||||
// Creates a buffered_file object associated with this file and detaches
|
||||
// this file object from the file.
|
||||
FMT_API buffered_file fdopen(const char* mode);
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
// A "C" numeric locale.
|
||||
class locale {
|
||||
private:
|
||||
# ifdef _WIN32
|
||||
using locale_t = _locale_t;
|
||||
|
||||
static void freelocale(locale_t loc) { _free_locale(loc); }
|
||||
|
||||
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
|
||||
return _strtod_l(nptr, endptr, loc);
|
||||
}
|
||||
# endif
|
||||
|
||||
locale_t locale_;
|
||||
|
||||
public:
|
||||
using type = locale_t;
|
||||
locale(const locale&) = delete;
|
||||
void operator=(const locale&) = delete;
|
||||
|
||||
locale() {
|
||||
# ifndef _WIN32
|
||||
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
|
||||
# else
|
||||
locale_ = _create_locale(LC_NUMERIC, "C");
|
||||
# endif
|
||||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
||||
}
|
||||
~locale() { freelocale(locale_); }
|
||||
|
||||
type get() const { return locale_; }
|
||||
|
||||
// Converts string to floating-point number and advances str past the end
|
||||
// of the parsed input.
|
||||
double strtod(const char*& str) const {
|
||||
char* end = nullptr;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
using Locale FMT_DEPRECATED_ALIAS = locale;
|
||||
#endif // FMT_LOCALE
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OS_H_
|
||||
143
oss/fmt/include/fmt/ostream.h
Normal file
143
oss/fmt/include/fmt/ostream.h
Normal file
@@ -0,0 +1,143 @@
|
||||
// Formatting library for C++ - std::ostream support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OSTREAM_H_
|
||||
#define FMT_OSTREAM_H_
|
||||
|
||||
#include <ostream>
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
|
||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||
private:
|
||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
||||
|
||||
buffer<Char>& buffer_;
|
||||
|
||||
public:
|
||||
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
||||
|
||||
protected:
|
||||
// The put-area is actually always empty. This makes the implementation
|
||||
// simpler and has the advantage that the streambuf and the buffer are always
|
||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||
// to overflow. There is no disadvantage here for sputn since this always
|
||||
// results in a call to xsputn.
|
||||
|
||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||
buffer_.push_back(static_cast<Char>(ch));
|
||||
return ch;
|
||||
}
|
||||
|
||||
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
||||
buffer_.append(s, s + count);
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||
private:
|
||||
// Hide all operator<< from std::basic_ostream<Char>.
|
||||
void_t<> operator<<(null<>);
|
||||
void_t<> operator<<(const Char*);
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
|
||||
!std::is_enum<T>::value)>
|
||||
void_t<> operator<<(T);
|
||||
};
|
||||
|
||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||
// std::ostream).
|
||||
template <typename T, typename Char> class is_streamable {
|
||||
private:
|
||||
template <typename U>
|
||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
||||
<< std::declval<U>()),
|
||||
void_t<>>::value>
|
||||
test(int);
|
||||
|
||||
template <typename> static std::false_type test(...);
|
||||
|
||||
using result = decltype(test<T>(0));
|
||||
|
||||
public:
|
||||
static const bool value = result::value;
|
||||
};
|
||||
|
||||
// Write the content of buf to os.
|
||||
template <typename Char>
|
||||
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
const Char* buf_data = buf.data();
|
||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||
unsigned_streamsize size = buf.size();
|
||||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
||||
do {
|
||||
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||
buf_data += n;
|
||||
size -= n;
|
||||
} while (size != 0);
|
||||
}
|
||||
|
||||
template <typename Char, typename T>
|
||||
void format_value(buffer<Char>& buf, const T& value,
|
||||
locale_ref loc = locale_ref()) {
|
||||
formatbuf<Char> format_buf(buf);
|
||||
std::basic_ostream<Char> output(&format_buf);
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
if (loc) output.imbue(loc.get<std::locale>());
|
||||
#endif
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
output << value;
|
||||
buf.resize(buf.size());
|
||||
}
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: formatter<basic_string_view<Char>, Char> {
|
||||
template <typename Context>
|
||||
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
format_value(buffer, value, ctx.locale());
|
||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
}
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::vformat_to(buffer, format_str, args);
|
||||
internal::write(os, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(cerr, "Don't {}!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||
vprint(os, to_string_view(format_str),
|
||||
internal::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OSTREAM_H_
|
||||
2
oss/fmt/include/fmt/posix.h
Normal file
2
oss/fmt/include/fmt/posix.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "os.h"
|
||||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
||||
721
oss/fmt/include/fmt/printf.h
Normal file
721
oss/fmt/include/fmt/printf.h
Normal file
@@ -0,0 +1,721 @@
|
||||
// Formatting library for C++ - legacy printf implementation
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_PRINTF_H_
|
||||
#define FMT_PRINTF_H_
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <limits> // std::numeric_limits
|
||||
|
||||
#include "ostream.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
|
||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||
// signed and unsigned integers.
|
||||
template <bool IsSigned> struct int_checker {
|
||||
template <typename T> static bool fits_in_int(T value) {
|
||||
unsigned max = max_value<int>();
|
||||
return value <= max;
|
||||
}
|
||||
static bool fits_in_int(bool) { return true; }
|
||||
};
|
||||
|
||||
template <> struct int_checker<true> {
|
||||
template <typename T> static bool fits_in_int(T value) {
|
||||
return value >= (std::numeric_limits<int>::min)() &&
|
||||
value <= max_value<int>();
|
||||
}
|
||||
static bool fits_in_int(int) { return true; }
|
||||
};
|
||||
|
||||
class printf_precision_handler {
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
int operator()(T value) {
|
||||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||
FMT_THROW(format_error("number is too big"));
|
||||
return (std::max)(static_cast<int>(value), 0);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
int operator()(T) {
|
||||
FMT_THROW(format_error("precision is not integer"));
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// An argument visitor that returns true iff arg is a zero integer.
|
||||
class is_zero_int {
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
bool operator()(T value) {
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
bool operator()(T) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||
|
||||
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
|
||||
|
||||
template <typename T, typename Context> class arg_converter {
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
basic_format_arg<Context>& arg_;
|
||||
char_type type_;
|
||||
|
||||
public:
|
||||
arg_converter(basic_format_arg<Context>& arg, char_type type)
|
||||
: arg_(arg), type_(type) {}
|
||||
|
||||
void operator()(bool value) {
|
||||
if (type_ != 's') operator()<bool>(value);
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
|
||||
void operator()(U value) {
|
||||
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
|
||||
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||
// Extra casts are used to silence warnings.
|
||||
if (is_signed) {
|
||||
arg_ = internal::make_arg<Context>(
|
||||
static_cast<int>(static_cast<target_type>(value)));
|
||||
} else {
|
||||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||
arg_ = internal::make_arg<Context>(
|
||||
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
||||
}
|
||||
} else {
|
||||
if (is_signed) {
|
||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||
// std::printf("%lld", -42); // prints "4294967254"
|
||||
// but we don't have to do the same because it's a UB.
|
||||
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
|
||||
} else {
|
||||
arg_ = internal::make_arg<Context>(
|
||||
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
|
||||
void operator()(U) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// Converts an integer argument to T for printf, if T is an integral type.
|
||||
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||
// unsigned).
|
||||
template <typename T, typename Context, typename Char>
|
||||
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
||||
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||
}
|
||||
|
||||
// Converts an integer argument to char for printf.
|
||||
template <typename Context> class char_converter {
|
||||
private:
|
||||
basic_format_arg<Context>& arg_;
|
||||
|
||||
public:
|
||||
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
void operator()(T value) {
|
||||
arg_ = internal::make_arg<Context>(
|
||||
static_cast<typename Context::char_type>(value));
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
void operator()(T) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// Checks if an argument is a valid printf width specifier and sets
|
||||
// left alignment if it is negative.
|
||||
template <typename Char> class printf_width_handler {
|
||||
private:
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
format_specs& specs_;
|
||||
|
||||
public:
|
||||
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
unsigned operator()(T value) {
|
||||
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||
if (internal::is_negative(value)) {
|
||||
specs_.align = align::left;
|
||||
width = 0 - width;
|
||||
}
|
||||
unsigned int_max = max_value<int>();
|
||||
if (width > int_max) FMT_THROW(format_error("number is too big"));
|
||||
return static_cast<unsigned>(width);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
unsigned operator()(T) {
|
||||
FMT_THROW(format_error("width is not integer"));
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Context>
|
||||
void printf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
Context(std::back_inserter(buf), format, args).format();
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char, typename Context>
|
||||
internal::truncating_iterator<OutputIt> printf(
|
||||
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
return Context(it, format, args).format();
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
using internal::printf; // For printing into memory_buffer.
|
||||
|
||||
template <typename Range> class printf_arg_formatter;
|
||||
|
||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||
|
||||
/**
|
||||
\rst
|
||||
The ``printf`` argument formatter.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Range>
|
||||
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
|
||||
public:
|
||||
using iterator = typename Range::iterator;
|
||||
|
||||
private:
|
||||
using char_type = typename Range::value_type;
|
||||
using base = internal::arg_formatter_base<Range>;
|
||||
using context_type = basic_printf_context<iterator, char_type>;
|
||||
|
||||
context_type& context_;
|
||||
|
||||
void write_null_pointer(char) {
|
||||
this->specs()->type = 0;
|
||||
this->write("(nil)");
|
||||
}
|
||||
|
||||
void write_null_pointer(wchar_t) {
|
||||
this->specs()->type = 0;
|
||||
this->write(L"(nil)");
|
||||
}
|
||||
|
||||
public:
|
||||
using format_specs = typename base::format_specs;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an argument formatter object.
|
||||
*buffer* is a reference to the output buffer and *specs* contains format
|
||||
specifier information for standard argument types.
|
||||
\endrst
|
||||
*/
|
||||
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
|
||||
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
|
||||
iterator operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
||||
// use std::is_same instead.
|
||||
if (std::is_same<T, bool>::value) {
|
||||
format_specs& fmt_specs = *this->specs();
|
||||
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
|
||||
fmt_specs.type = 0;
|
||||
this->write(value != 0);
|
||||
} else if (std::is_same<T, char_type>::value) {
|
||||
format_specs& fmt_specs = *this->specs();
|
||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
||||
return (*this)(static_cast<int>(value));
|
||||
fmt_specs.sign = sign::none;
|
||||
fmt_specs.alt = false;
|
||||
fmt_specs.align = align::right;
|
||||
return base::operator()(value);
|
||||
} else {
|
||||
return base::operator()(value);
|
||||
}
|
||||
return this->out();
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
iterator operator()(T value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated C string. */
|
||||
iterator operator()(const char* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->specs()->type == 'p')
|
||||
write_null_pointer(char_type());
|
||||
else
|
||||
this->write("(null)");
|
||||
return this->out();
|
||||
}
|
||||
|
||||
/** Formats a null-terminated wide C string. */
|
||||
iterator operator()(const wchar_t* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->specs()->type == 'p')
|
||||
write_null_pointer(char_type());
|
||||
else
|
||||
this->write(L"(null)");
|
||||
return this->out();
|
||||
}
|
||||
|
||||
iterator operator()(basic_string_view<char_type> value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
iterator operator()(monostate value) { return base::operator()(value); }
|
||||
|
||||
/** Formats a pointer. */
|
||||
iterator operator()(const void* value) {
|
||||
if (value) return base::operator()(value);
|
||||
this->specs()->type = 0;
|
||||
write_null_pointer(char_type());
|
||||
return this->out();
|
||||
}
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
handle.format(context_.parse_context(), context_);
|
||||
return this->out();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct printf_formatter {
|
||||
printf_formatter() = delete;
|
||||
|
||||
template <typename ParseContext>
|
||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
internal::format_value(internal::get_container(ctx.out()), value);
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
/** This template formats data and writes the output to a writer. */
|
||||
template <typename OutputIt, typename Char> class basic_printf_context {
|
||||
public:
|
||||
/** The character type for the output. */
|
||||
using char_type = Char;
|
||||
using iterator = OutputIt;
|
||||
using format_arg = basic_format_arg<basic_printf_context>;
|
||||
template <typename T> using formatter_type = printf_formatter<T>;
|
||||
|
||||
private:
|
||||
using format_specs = basic_format_specs<char_type>;
|
||||
|
||||
OutputIt out_;
|
||||
basic_format_args<basic_printf_context> args_;
|
||||
basic_format_parse_context<Char> parse_ctx_;
|
||||
|
||||
static void parse_flags(format_specs& specs, const Char*& it,
|
||||
const Char* end);
|
||||
|
||||
// Returns the argument with specified index or, if arg_index is -1, the next
|
||||
// argument.
|
||||
format_arg get_arg(int arg_index = -1);
|
||||
|
||||
// Parses argument index, flags and width and returns the argument index.
|
||||
int parse_header(const Char*& it, const Char* end, format_specs& specs);
|
||||
|
||||
public:
|
||||
/**
|
||||
\rst
|
||||
Constructs a ``printf_context`` object. References to the arguments and
|
||||
the writer are stored in the context object so make sure they have
|
||||
appropriate lifetimes.
|
||||
\endrst
|
||||
*/
|
||||
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
||||
basic_format_args<basic_printf_context> args)
|
||||
: out_(out), args_(args), parse_ctx_(format_str) {}
|
||||
|
||||
OutputIt out() { return out_; }
|
||||
void advance_to(OutputIt it) { out_ = it; }
|
||||
|
||||
internal::locale_ref locale() { return {}; }
|
||||
|
||||
format_arg arg(int id) const { return args_.get(id); }
|
||||
|
||||
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
parse_ctx_.on_error(message);
|
||||
}
|
||||
|
||||
/** Formats stored arguments and writes the output to the range. */
|
||||
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
|
||||
OutputIt format();
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
||||
const Char*& it,
|
||||
const Char* end) {
|
||||
for (; it != end; ++it) {
|
||||
switch (*it) {
|
||||
case '-':
|
||||
specs.align = align::left;
|
||||
break;
|
||||
case '+':
|
||||
specs.sign = sign::plus;
|
||||
break;
|
||||
case '0':
|
||||
specs.fill[0] = '0';
|
||||
break;
|
||||
case ' ':
|
||||
specs.sign = sign::space;
|
||||
break;
|
||||
case '#':
|
||||
specs.alt = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
typename basic_printf_context<OutputIt, Char>::format_arg
|
||||
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
|
||||
if (arg_index < 0)
|
||||
arg_index = parse_ctx_.next_arg_id();
|
||||
else
|
||||
parse_ctx_.check_arg_id(--arg_index);
|
||||
return internal::get_arg(*this, arg_index);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
||||
const Char* end,
|
||||
format_specs& specs) {
|
||||
int arg_index = -1;
|
||||
char_type c = *it;
|
||||
if (c >= '0' && c <= '9') {
|
||||
// Parse an argument index (if followed by '$') or a width possibly
|
||||
// preceded with '0' flag(s).
|
||||
internal::error_handler eh;
|
||||
int value = parse_nonnegative_int(it, end, eh);
|
||||
if (it != end && *it == '$') { // value is an argument index
|
||||
++it;
|
||||
arg_index = value;
|
||||
} else {
|
||||
if (c == '0') specs.fill[0] = '0';
|
||||
if (value != 0) {
|
||||
// Nonzero value means that we parsed width and don't need to
|
||||
// parse it or flags again, so return now.
|
||||
specs.width = value;
|
||||
return arg_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_flags(specs, it, end);
|
||||
// Parse width.
|
||||
if (it != end) {
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
internal::error_handler eh;
|
||||
specs.width = parse_nonnegative_int(it, end, eh);
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
specs.width = static_cast<int>(visit_format_arg(
|
||||
internal::printf_width_handler<char_type>(specs), get_arg()));
|
||||
}
|
||||
}
|
||||
return arg_index;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
template <typename ArgFormatter>
|
||||
OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||
auto out = this->out();
|
||||
const Char* start = parse_ctx_.begin();
|
||||
const Char* end = parse_ctx_.end();
|
||||
auto it = start;
|
||||
while (it != end) {
|
||||
char_type c = *it++;
|
||||
if (c != '%') continue;
|
||||
if (it != end && *it == c) {
|
||||
out = std::copy(start, it, out);
|
||||
start = ++it;
|
||||
continue;
|
||||
}
|
||||
out = std::copy(start, it - 1, out);
|
||||
|
||||
format_specs specs;
|
||||
specs.align = align::right;
|
||||
|
||||
// Parse argument index, flags and width.
|
||||
int arg_index = parse_header(it, end, specs);
|
||||
if (arg_index == 0) on_error("argument index out of range");
|
||||
|
||||
// Parse precision.
|
||||
if (it != end && *it == '.') {
|
||||
++it;
|
||||
c = it != end ? *it : 0;
|
||||
if ('0' <= c && c <= '9') {
|
||||
internal::error_handler eh;
|
||||
specs.precision = parse_nonnegative_int(it, end, eh);
|
||||
} else if (c == '*') {
|
||||
++it;
|
||||
specs.precision = static_cast<int>(
|
||||
visit_format_arg(internal::printf_precision_handler(), get_arg()));
|
||||
} else {
|
||||
specs.precision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
format_arg arg = get_arg(arg_index);
|
||||
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
|
||||
specs.alt = false;
|
||||
if (specs.fill[0] == '0') {
|
||||
if (arg.is_arithmetic())
|
||||
specs.align = align::numeric;
|
||||
else
|
||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
|
||||
}
|
||||
|
||||
// Parse length and convert the argument to the required type.
|
||||
c = it != end ? *it++ : 0;
|
||||
char_type t = it != end ? *it : 0;
|
||||
using internal::convert_arg;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
if (t == 'h') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<signed char>(arg, t);
|
||||
} else {
|
||||
convert_arg<short>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (t == 'l') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<long long>(arg, t);
|
||||
} else {
|
||||
convert_arg<long>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
convert_arg<intmax_t>(arg, t);
|
||||
break;
|
||||
case 'z':
|
||||
convert_arg<std::size_t>(arg, t);
|
||||
break;
|
||||
case 't':
|
||||
convert_arg<std::ptrdiff_t>(arg, t);
|
||||
break;
|
||||
case 'L':
|
||||
// printf produces garbage when 'L' is omitted for long double, no
|
||||
// need to do the same.
|
||||
break;
|
||||
default:
|
||||
--it;
|
||||
convert_arg<void>(arg, c);
|
||||
}
|
||||
|
||||
// Parse type.
|
||||
if (it == end) FMT_THROW(format_error("invalid format string"));
|
||||
specs.type = static_cast<char>(*it++);
|
||||
if (arg.is_integral()) {
|
||||
// Normalize type.
|
||||
switch (specs.type) {
|
||||
case 'i':
|
||||
case 'u':
|
||||
specs.type = 'd';
|
||||
break;
|
||||
case 'c':
|
||||
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
|
||||
arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
start = it;
|
||||
|
||||
// Format argument.
|
||||
visit_format_arg(ArgFormatter(out, specs, *this), arg);
|
||||
}
|
||||
return std::copy(start, it, out);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
using basic_printf_context_t =
|
||||
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
|
||||
Char>;
|
||||
|
||||
using printf_context = basic_printf_context_t<char>;
|
||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||
|
||||
using printf_args = basic_format_args<printf_context>;
|
||||
using wprintf_args = basic_format_args<wprintf_context>;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline format_arg_store<printf_context, Args...> make_printf_args(
|
||||
const Args&... args) {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
|
||||
const Args&... args) {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vsprintf(
|
||||
const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
printf(buffer, to_string_view(format), args);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vsprintf(to_string_view(format), make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vfprintf(
|
||||
std::FILE* f, const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
printf(buffer, to_string_view(format), args);
|
||||
std::size_t size = buffer.size();
|
||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||
? -1
|
||||
: static_cast<int>(size);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the file *f*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vfprintf(f, to_string_view(format),
|
||||
make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vprintf(
|
||||
const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
return vfprintf(stdout, to_string_view(format), args);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to ``stdout``.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||
inline int printf(const S& format_str, const Args&... args) {
|
||||
using context = basic_printf_context_t<char_t<S>>;
|
||||
return vprintf(to_string_view(format_str),
|
||||
make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vfprintf(
|
||||
std::basic_ostream<Char>& os, const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
printf(buffer, to_string_view(format), args);
|
||||
internal::write(os, buffer);
|
||||
return static_cast<int>(buffer.size());
|
||||
}
|
||||
|
||||
/** Formats arguments and writes the output to the range. */
|
||||
template <typename ArgFormatter, typename Char,
|
||||
typename Context =
|
||||
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
||||
typename ArgFormatter::iterator vprintf(
|
||||
internal::buffer<Char>& out, basic_string_view<Char> format_str,
|
||||
basic_format_args<type_identity_t<Context>> args) {
|
||||
typename ArgFormatter::iterator iter(out);
|
||||
Context(iter, format_str, args).template format<ArgFormatter>();
|
||||
return iter;
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::fprintf(cerr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
|
||||
const Args&... args) {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vfprintf(os, to_string_view(format_str),
|
||||
make_format_args<context>(args...));
|
||||
}
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_PRINTF_H_
|
||||
387
oss/fmt/include/fmt/ranges.h
Normal file
387
oss/fmt/include/fmt/ranges.h
Normal file
@@ -0,0 +1,387 @@
|
||||
// Formatting library for C++ - experimental range support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
//
|
||||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||
// All Rights Reserved
|
||||
// {fmt} support for ranges, containers and types tuple interface.
|
||||
|
||||
#ifndef FMT_RANGES_H_
|
||||
#define FMT_RANGES_H_
|
||||
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
// output only up to N items from the range.
|
||||
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
||||
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename Char> struct formatting_base {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_range : formatting_base<Char> {
|
||||
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||
// range.
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_tuple : formatting_base<Char> {
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename RangeT, typename OutputIterator>
|
||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||
*out++ = *it;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(const char* str, OutputIterator out) {
|
||||
while (*str) *out++ = *str++;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(char ch, OutputIterator out) {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Return true value if T has std::string interface, like std::string_view.
|
||||
template <typename T> class is_like_std_string {
|
||||
template <typename U>
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
||||
|
||||
template <typename... Ts> struct conditional_helper {};
|
||||
|
||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||
template <typename T>
|
||||
struct is_range_<
|
||||
T, conditional_t<false,
|
||||
conditional_helper<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>,
|
||||
void>> : std::true_type {};
|
||||
#endif
|
||||
|
||||
/// tuple_size and tuple_element check.
|
||||
template <typename T> class is_tuple_like_ {
|
||||
template <typename U>
|
||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
// Check for integer_sequence
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||
template <typename T, T... N>
|
||||
using integer_sequence = std::integer_sequence<T, N...>;
|
||||
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||
template <std::size_t N>
|
||||
using make_index_sequence = std::make_index_sequence<N>;
|
||||
#else
|
||||
template <typename T, T... N> struct integer_sequence {
|
||||
using value_type = T;
|
||||
|
||||
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
|
||||
};
|
||||
|
||||
template <std::size_t... N>
|
||||
using index_sequence = integer_sequence<std::size_t, N...>;
|
||||
|
||||
template <typename T, std::size_t N, T... Ns>
|
||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||
template <typename T, T... Ns>
|
||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||
|
||||
template <std::size_t N>
|
||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||
#endif
|
||||
|
||||
template <class Tuple, class F, size_t... Is>
|
||||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
|
||||
using std::get;
|
||||
// using free function get<I>(T) now.
|
||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||
(void)_; // blocks warnings
|
||||
}
|
||||
|
||||
template <class T>
|
||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
|
||||
T const&) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||
const auto indexes = get_indexes(tup);
|
||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " {}" : "{}";
|
||||
}
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||
return add_space ? " '{}'" : "'{}'";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||
return add_space ? L" '{}'" : L"'{}'";
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T> struct is_tuple_like {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||
};
|
||||
|
||||
template <typename TupleT, typename Char>
|
||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
private:
|
||||
// C++11 generic lambda for format()
|
||||
template <typename FormatContext> struct format_each {
|
||||
template <typename T> void operator()(const T& v) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), v),
|
||||
v);
|
||||
++i;
|
||||
}
|
||||
|
||||
formatting_tuple<Char>& formatting;
|
||||
std::size_t& i;
|
||||
typename std::add_lvalue_reference<decltype(
|
||||
std::declval<FormatContext>().out())>::type out;
|
||||
};
|
||||
|
||||
public:
|
||||
formatting_tuple<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
std::size_t i = 0;
|
||||
internal::copy(formatting.prefix, out);
|
||||
|
||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
internal::copy(formatting.postfix, out);
|
||||
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
internal::is_range_<T>::value &&
|
||||
!internal::is_like_std_string<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_constructible<internal::std_string_view<Char>, T>::value;
|
||||
};
|
||||
|
||||
template <typename RangeT, typename Char>
|
||||
struct formatter<RangeT, Char,
|
||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
||||
formatting_range<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(const RangeT& values,
|
||||
FormatContext& ctx) {
|
||||
auto out = internal::copy(formatting.prefix, ctx.out());
|
||||
std::size_t i = 0;
|
||||
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||
*it);
|
||||
if (++i > formatting.range_length_limit) {
|
||||
out = format_to(out, " ... <other elements>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
return internal::copy(formatting.postfix, out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple{t}, sep{s} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
||||
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename FormatContext, size_t... N>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
internal::index_sequence<N...>) {
|
||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||
// can be replaced with a constexpr branch in the variadic overload.
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename Arg, typename... Args>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
const Arg& arg, const Args&... args) {
|
||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||
auto out = ctx.out();
|
||||
out = base{}.format(arg, ctx);
|
||||
if (sizeof...(Args) > 0) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
return format_args(value, ctx, args...);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::tuple<int, char> t = {1, 'a'};
|
||||
fmt::print("{}", fmt::join(t, ", "));
|
||||
// Output: "1, a"
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
||||
string_view sep) {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||
wstring_view sep) {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `initializer_list` with elements separated by
|
||||
`sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
||||
// Output: "1, 2, 3"
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
|
||||
std::initializer_list<T> list, string_view sep) {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
|
||||
std::initializer_list<T> list, wstring_view sep) {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_RANGES_H_
|
||||
176
oss/fmt/src/format.cc
Normal file
176
oss/fmt/src/format.cc
Normal file
@@ -0,0 +1,176 @@
|
||||
// Formatting library for C++
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/format-inl.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||
T value) {
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
if (precision > 100000)
|
||||
throw std::runtime_error(
|
||||
"fuzz mode - avoid large allocation inside snprintf");
|
||||
#endif
|
||||
// Suppress the warning about nonliteral format string.
|
||||
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||
: snprintf_ptr(buf, size, format, precision, value);
|
||||
}
|
||||
struct sprintf_specs {
|
||||
int precision;
|
||||
char type;
|
||||
bool alt : 1;
|
||||
|
||||
template <typename Char>
|
||||
constexpr sprintf_specs(basic_format_specs<Char> specs)
|
||||
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
|
||||
|
||||
constexpr bool has_precision() const { return precision >= 0; }
|
||||
};
|
||||
|
||||
// This is deprecated and is kept only to preserve ABI compatibility.
|
||||
template <typename Double>
|
||||
char* sprintf_format(Double value, internal::buffer<char>& buf,
|
||||
sprintf_specs specs) {
|
||||
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
||||
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
|
||||
|
||||
// Build format string.
|
||||
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
|
||||
char format[max_format_size];
|
||||
char* format_ptr = format;
|
||||
*format_ptr++ = '%';
|
||||
if (specs.alt || !specs.type) *format_ptr++ = '#';
|
||||
if (specs.precision >= 0) {
|
||||
*format_ptr++ = '.';
|
||||
*format_ptr++ = '*';
|
||||
}
|
||||
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
|
||||
|
||||
char type = specs.type;
|
||||
|
||||
if (type == '%')
|
||||
type = 'f';
|
||||
else if (type == 0 || type == 'n')
|
||||
type = 'g';
|
||||
#if FMT_MSC_VER
|
||||
if (type == 'F') {
|
||||
// MSVC's printf doesn't support 'F'.
|
||||
type = 'f';
|
||||
}
|
||||
#endif
|
||||
*format_ptr++ = type;
|
||||
*format_ptr = '\0';
|
||||
|
||||
// Format using snprintf.
|
||||
char* start = nullptr;
|
||||
char* decimal_point_pos = nullptr;
|
||||
for (;;) {
|
||||
std::size_t buffer_size = buf.capacity();
|
||||
start = &buf[0];
|
||||
int result =
|
||||
format_float(start, buffer_size, format, specs.precision, value);
|
||||
if (result >= 0) {
|
||||
unsigned n = internal::to_unsigned(result);
|
||||
if (n < buf.capacity()) {
|
||||
// Find the decimal point.
|
||||
auto p = buf.data(), end = p + n;
|
||||
if (*p == '+' || *p == '-') ++p;
|
||||
if (specs.type != 'a' && specs.type != 'A') {
|
||||
while (p < end && *p >= '0' && *p <= '9') ++p;
|
||||
if (p < end && *p != 'e' && *p != 'E') {
|
||||
decimal_point_pos = p;
|
||||
if (!specs.type) {
|
||||
// Keep only one trailing zero after the decimal point.
|
||||
++p;
|
||||
if (*p == '0') ++p;
|
||||
while (p != end && *p >= '1' && *p <= '9') ++p;
|
||||
char* where = p;
|
||||
while (p != end && *p == '0') ++p;
|
||||
if (p == end || *p < '0' || *p > '9') {
|
||||
if (p != end) std::memmove(where, p, to_unsigned(end - p));
|
||||
n -= static_cast<unsigned>(p - where);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.resize(n);
|
||||
break; // The buffer is large enough - continue with formatting.
|
||||
}
|
||||
buf.reserve(n + 1);
|
||||
} else {
|
||||
// If result is negative we ask to increase the capacity by at least 1,
|
||||
// but as std::vector, the buffer grows exponentially.
|
||||
buf.reserve(buf.capacity() + 1);
|
||||
}
|
||||
}
|
||||
return decimal_point_pos;
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
|
||||
sprintf_specs);
|
||||
template FMT_API char* internal::sprintf_format(long double,
|
||||
internal::buffer<char>&,
|
||||
sprintf_specs);
|
||||
|
||||
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
|
||||
|
||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||
int (*instantiate_format_float)(double, int, internal::float_specs,
|
||||
internal::buffer<char>&) =
|
||||
internal::format_float;
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
|
||||
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
|
||||
#endif
|
||||
|
||||
// Explicit instantiations for char.
|
||||
|
||||
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
|
||||
template FMT_API char internal::thousands_sep_impl(locale_ref);
|
||||
template FMT_API char internal::decimal_point_impl(locale_ref);
|
||||
|
||||
template FMT_API void internal::buffer<char>::append(const char*, const char*);
|
||||
|
||||
template FMT_API void internal::arg_map<format_context>::init(
|
||||
const basic_format_args<format_context>& args);
|
||||
|
||||
template FMT_API std::string internal::vformat<char>(
|
||||
string_view, basic_format_args<format_context>);
|
||||
|
||||
template FMT_API format_context::iterator internal::vformat_to(
|
||||
internal::buffer<char>&, string_view, basic_format_args<format_context>);
|
||||
|
||||
template FMT_API int internal::snprintf_float(double, int,
|
||||
internal::float_specs,
|
||||
internal::buffer<char>&);
|
||||
template FMT_API int internal::snprintf_float(long double, int,
|
||||
internal::float_specs,
|
||||
internal::buffer<char>&);
|
||||
template FMT_API int internal::format_float(double, int, internal::float_specs,
|
||||
internal::buffer<char>&);
|
||||
template FMT_API int internal::format_float(long double, int,
|
||||
internal::float_specs,
|
||||
internal::buffer<char>&);
|
||||
|
||||
// Explicit instantiations for wchar_t.
|
||||
|
||||
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
|
||||
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
|
||||
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
|
||||
|
||||
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
|
||||
const wchar_t*);
|
||||
|
||||
template FMT_API std::wstring internal::vformat<wchar_t>(
|
||||
wstring_view, basic_format_args<wformat_context>);
|
||||
FMT_END_NAMESPACE
|
||||
316
oss/fmt/src/os.cc
Normal file
316
oss/fmt/src/os.cc
Normal file
@@ -0,0 +1,316 @@
|
||||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "fmt/os.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
|
||||
# ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
# else
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <io.h>
|
||||
# include <windows.h>
|
||||
|
||||
# define O_CREAT _O_CREAT
|
||||
# define O_TRUNC _O_TRUNC
|
||||
|
||||
# ifndef S_IRUSR
|
||||
# define S_IRUSR _S_IREAD
|
||||
# endif
|
||||
|
||||
# ifndef S_IWUSR
|
||||
# define S_IWUSR _S_IWRITE
|
||||
# endif
|
||||
|
||||
# ifdef __MINGW32__
|
||||
# define _SH_DENYNO 0x40
|
||||
# endif
|
||||
# endif // _WIN32
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef fileno
|
||||
# undef fileno
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
// Return type of read and write functions.
|
||||
using RWResult = int;
|
||||
|
||||
// On Windows the count argument to read and write is unsigned, so convert
|
||||
// it from size_t preventing integer overflow.
|
||||
inline unsigned convert_rwcount(std::size_t count) {
|
||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||
}
|
||||
#else
|
||||
// Return type of read and write functions.
|
||||
using RWResult = ssize_t;
|
||||
|
||||
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef _WIN32
|
||||
internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
||||
if (int error_code = convert(s)) {
|
||||
FMT_THROW(windows_error(error_code,
|
||||
"cannot convert string from UTF-16 to UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
int internal::utf16_to_utf8::convert(wstring_view s) {
|
||||
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
|
||||
int s_size = static_cast<int>(s.size());
|
||||
if (s_size == 0) {
|
||||
// WideCharToMultiByte does not support zero length, handle separately.
|
||||
buffer_.resize(1);
|
||||
buffer_[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
|
||||
nullptr, nullptr);
|
||||
if (length == 0) return GetLastError();
|
||||
buffer_.resize(length + 1);
|
||||
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
|
||||
length, nullptr, nullptr);
|
||||
if (length == 0) return GetLastError();
|
||||
buffer_[length] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void windows_error::init(int err_code, string_view format_str,
|
||||
format_args args) {
|
||||
error_code_ = err_code;
|
||||
memory_buffer buffer;
|
||||
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
|
||||
std::runtime_error& base = *this;
|
||||
base = std::runtime_error(to_string(buffer));
|
||||
}
|
||||
|
||||
void internal::format_windows_error(internal::buffer<char>& out, int error_code,
|
||||
string_view message) FMT_NOEXCEPT {
|
||||
FMT_TRY {
|
||||
wmemory_buffer buf;
|
||||
buf.resize(inline_buffer_size);
|
||||
for (;;) {
|
||||
wchar_t* system_message = &buf[0];
|
||||
int result = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
|
||||
static_cast<uint32_t>(buf.size()), nullptr);
|
||||
if (result != 0) {
|
||||
utf16_to_utf8 utf8_message;
|
||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||
internal::writer w(out);
|
||||
w.write(message);
|
||||
w.write(": ");
|
||||
w.write(utf8_message);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
break; // Can't get error message, report error code instead.
|
||||
buf.resize(buf.size() * 2);
|
||||
}
|
||||
}
|
||||
FMT_CATCH(...) {}
|
||||
format_error_code(out, error_code, message);
|
||||
}
|
||||
|
||||
void report_windows_error(int error_code,
|
||||
fmt::string_view message) FMT_NOEXCEPT {
|
||||
report_error(internal::format_windows_error, error_code, message);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
buffered_file::~buffered_file() FMT_NOEXCEPT {
|
||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
|
||||
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
|
||||
nullptr);
|
||||
if (!file_)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
||||
}
|
||||
|
||||
void buffered_file::close() {
|
||||
if (!file_) return;
|
||||
int result = FMT_SYSTEM(fclose(file_));
|
||||
file_ = nullptr;
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||
#define FMT_ARGS
|
||||
|
||||
int buffered_file::fileno() const {
|
||||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||
return fd;
|
||||
}
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
file::file(cstring_view path, int oflag) {
|
||||
int mode = S_IRUSR | S_IWUSR;
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
fd_ = -1;
|
||||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||
# else
|
||||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
||||
# endif
|
||||
if (fd_ == -1)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
||||
}
|
||||
|
||||
file::~file() FMT_NOEXCEPT {
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
void file::close() {
|
||||
if (fd_ == -1) return;
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
int result = FMT_POSIX_CALL(close(fd_));
|
||||
fd_ = -1;
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
long long file::size() const {
|
||||
# ifdef _WIN32
|
||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||
// Both functions support large file sizes.
|
||||
DWORD size_upper = 0;
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
|
||||
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
|
||||
if (size_lower == INVALID_FILE_SIZE) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != NO_ERROR)
|
||||
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
|
||||
}
|
||||
unsigned long long long_size = size_upper;
|
||||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||
# else
|
||||
using Stat = struct stat;
|
||||
Stat file_stat = Stat();
|
||||
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
||||
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
||||
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
|
||||
"return type of file::size is not large enough");
|
||||
return file_stat.st_size;
|
||||
# endif
|
||||
}
|
||||
|
||||
std::size_t file::read(void* buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
|
||||
return internal::to_unsigned(result);
|
||||
}
|
||||
|
||||
std::size_t file::write(const void* buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||
return internal::to_unsigned(result);
|
||||
}
|
||||
|
||||
file file::dup(int fd) {
|
||||
// Don't retry as dup doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||
if (new_fd == -1)
|
||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
||||
return file(new_fd);
|
||||
}
|
||||
|
||||
void file::dup2(int fd) {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) {
|
||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
|
||||
fd_, fd));
|
||||
}
|
||||
}
|
||||
|
||||
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) ec = error_code(errno);
|
||||
}
|
||||
|
||||
void file::pipe(file& read_end, file& write_end) {
|
||||
// Close the descriptors first to make sure that assignments don't throw
|
||||
// and there are no leaks.
|
||||
read_end.close();
|
||||
write_end.close();
|
||||
int fds[2] = {};
|
||||
# ifdef _WIN32
|
||||
// Make the default pipe capacity same as on Linux 2.6.11+.
|
||||
enum { DEFAULT_CAPACITY = 65536 };
|
||||
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
|
||||
# else
|
||||
// Don't retry as the pipe function doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
|
||||
int result = FMT_POSIX_CALL(pipe(fds));
|
||||
# endif
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
|
||||
// The following assignments don't throw because read_fd and write_fd
|
||||
// are closed.
|
||||
read_end = file(fds[0]);
|
||||
write_end = file(fds[1]);
|
||||
}
|
||||
|
||||
buffered_file file::fdopen(const char* mode) {
|
||||
// Don't retry as fdopen doesn't return EINTR.
|
||||
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||
if (!f)
|
||||
FMT_THROW(
|
||||
system_error(errno, "cannot associate stream with file descriptor"));
|
||||
buffered_file bf(f);
|
||||
fd_ = -1;
|
||||
return bf;
|
||||
}
|
||||
|
||||
long getpagesize() {
|
||||
# ifdef _WIN32
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return si.dwPageSize;
|
||||
# else
|
||||
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
||||
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
|
||||
return size;
|
||||
# endif
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
FMT_END_NAMESPACE
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user