Compare commits
6 Commits
v1.1.1671.
...
dev/miniks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa3c3abd29 | ||
|
|
181de80515 | ||
|
|
b83812349a | ||
|
|
0006a5b91b | ||
|
|
769f6aca8c | ||
|
|
1c7aad82a9 |
8
.github/ISSUE_TEMPLATE/Bug_Report.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: "Bug report 🐛"
|
||||
name: Bug report 🐛
|
||||
about: Report errors or unexpected behavior
|
||||
title: ''
|
||||
title: "Bug Report (IF I DO NOT CHANGE THIS THE ISSUE WILL BE AUTO-CLOSED)"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
@@ -26,8 +26,6 @@ This bug tracker is monitored by Windows Terminal development team and other tec
|
||||
**Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to Github issues**.
|
||||
Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue.
|
||||
|
||||
If this is an application crash, please also provide a Feedback Hub submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal (Preview)" and choose "Share My Feedback" after submission to get the link.
|
||||
|
||||
Please use this form and describe your issue, concisely but precisely, with as much detail as possible.
|
||||
|
||||
-->
|
||||
@@ -35,7 +33,7 @@ Please use this form and describe your issue, concisely but precisely, with as m
|
||||
# Environment
|
||||
|
||||
```none
|
||||
Windows build number: [run `[Environment]::OSVersion` for powershell, or `ver` for cmd]
|
||||
Windows build number: [run "ver" at a command prompt]
|
||||
Windows Terminal version (if applicable):
|
||||
|
||||
Any other software?
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: "Documentation Issue 📚"
|
||||
name: Documentation Issue 📚
|
||||
about: Report issues in our documentation
|
||||
title: ''
|
||||
title: "Documentation Issue"
|
||||
labels: Issue-Docs
|
||||
assignees: ''
|
||||
|
||||
|
||||
69
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
@@ -1,35 +1,34 @@
|
||||
---
|
||||
name: "Feature Request/Idea 🚀"
|
||||
about: Suggest a new feature or improvement (this does not mean you have to implement
|
||||
it)
|
||||
title: ''
|
||||
labels: Issue-Feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
||||
|
||||
I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
|
||||
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
|
||||
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
|
||||
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
|
||||
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
|
||||
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.
|
||||
|
||||
All good? Then proceed!
|
||||
-->
|
||||
|
||||
# Description of the new feature/enhancement
|
||||
|
||||
<!--
|
||||
A clear and concise description of what the problem is that the new feature would solve.
|
||||
Describe why and how a user would use this new functionality (if applicable).
|
||||
-->
|
||||
|
||||
# Proposed technical implementation details (optional)
|
||||
|
||||
<!--
|
||||
A clear and concise description of what you want to happen.
|
||||
-->
|
||||
---
|
||||
name: Feature Request/Idea 🚀
|
||||
about: Suggest a new feature or improvement (this does not mean you have to implement it)
|
||||
title: "Feature Request"
|
||||
labels: Issue-Feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
||||
|
||||
I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
|
||||
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
|
||||
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
|
||||
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
|
||||
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
|
||||
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.
|
||||
|
||||
All good? Then proceed!
|
||||
-->
|
||||
|
||||
# Summary of the new feature/enhancement
|
||||
|
||||
<!--
|
||||
A clear and concise description of what the problem is that the new feature would solve.
|
||||
Describe why and how a user would use this new functionality (if applicable).
|
||||
-->
|
||||
|
||||
# Proposed technical implementation details (optional)
|
||||
|
||||
<!--
|
||||
A clear and concise description of what you want to happen.
|
||||
-->
|
||||
|
||||
10
.github/ISSUE_TEMPLATE/Guidance_Issue.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Community Guidance Request ✨
|
||||
about: Suggest somewhere the Windows Terminal Team needs to provide community guidance through new documentation or process.
|
||||
title: "Guidance"
|
||||
labels: Issue-Docs
|
||||
assignees: 'bitcrazed'
|
||||
|
||||
---
|
||||
|
||||
<!-- What needs to change? Who is responsible for it? Why is it an open question? -->
|
||||
25
.github/actions/spell-check/advice.txt
vendored
@@ -1,25 +0,0 @@
|
||||
<details>
|
||||
<summary>
|
||||
:pencil2: Contributor please read this
|
||||
</summary>
|
||||
|
||||
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
|
||||
|
||||
:warning: The command is written for posix shells. You can copy the contents of each `perl` command excluding the outer `'` marks and dropping any `'"`/`"'` quotation mark pairs into a file and then run `perl file.pl` from the root of the repository to run the code. Alternatively, you can manually insert the items...
|
||||
|
||||
If the listed items are:
|
||||
* ... **misspelled**, then please *correct* them instead of using the command.
|
||||
* ... *names*, please add them to `.github/actions/spell-check/dictionary/names.txt`.
|
||||
* ... APIs, you can add them to a file in `.github/actions/spell-check/dictionary/`.
|
||||
* ... just things you're using, please add them to an appropriate file in `.github/actions/spell-check/expect/`.
|
||||
* ... tokens you only need in one place and shouldn't *generally be used*, you can add an item in an appropriate file in `.github/actions/spell-check/patterns/`.
|
||||
|
||||
See the `README.md` in each directory for more information.
|
||||
|
||||
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [:check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
|
||||
|
||||
</details>
|
||||
|
||||
#### :warning: Reviewers
|
||||
At present, the action that triggered this message will not show its :x: in this PR unless the branch is within this repository.
|
||||
Thus, you **should** make sure that this comment has been addressed before encouraging the merge bot to merge this PR.
|
||||
20
.github/actions/spell-check/dictionary/README.md
vendored
@@ -1,20 +0,0 @@
|
||||
# Dictionaries are lists of words to accept unconditionally
|
||||
|
||||
While check spelling will complain about a whitelisted word
|
||||
which is no longer present, you can include things here even if
|
||||
they are not otherwise present in the repository.
|
||||
|
||||
E.g., you could include a list of system APIs here, or potential
|
||||
contributors (so that if a future commit includes their name,
|
||||
it'll be accepted).
|
||||
|
||||
### Files
|
||||
|
||||
| File | Description |
|
||||
| ---- | ----------- |
|
||||
| [Dictionary](dictionary.txt) | Primary US English dictionary |
|
||||
| [Chinese](chinese.txt) | Chinese words |
|
||||
| [Japanese](japanese.txt) | Japanese words |
|
||||
| [Microsoft](microsoft.txt) | Microsoft brand items |
|
||||
| [Fonts](fonts.txt) | Font names |
|
||||
| [Names](names.txt) | Names of people |
|
||||
35
.github/actions/spell-check/dictionary/apis.txt
vendored
@@ -1,35 +0,0 @@
|
||||
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
|
||||
@@ -1,5 +0,0 @@
|
||||
CHINESEBIG
|
||||
choseong
|
||||
Jongseong
|
||||
Jungseong
|
||||
ssangtikeut
|
||||
479826
.github/actions/spell-check/dictionary/dictionary.txt
vendored
@@ -1,8 +0,0 @@
|
||||
Consolas
|
||||
emoji
|
||||
Extralight
|
||||
Gabriola
|
||||
Iosevka
|
||||
MDL
|
||||
Monofur
|
||||
Segoe
|
||||
@@ -1,4 +0,0 @@
|
||||
arigatoo
|
||||
doomo
|
||||
Kaomojis
|
||||
TATEGAKI
|
||||
@@ -1,3 +0,0 @@
|
||||
powf
|
||||
sqrtf
|
||||
isnan
|
||||
@@ -1,14 +0,0 @@
|
||||
ACLs
|
||||
DACL
|
||||
DACLs
|
||||
LKG
|
||||
mfcribbon
|
||||
microsoft
|
||||
microsoftonline
|
||||
osgvsowi
|
||||
powerrename
|
||||
powershell
|
||||
SACLs
|
||||
tdbuildteamid
|
||||
vcruntime
|
||||
visualstudio
|
||||
61
.github/actions/spell-check/dictionary/names.txt
vendored
@@ -1,61 +0,0 @@
|
||||
Anup
|
||||
austdi
|
||||
Ballmer
|
||||
bhoj
|
||||
Bhojwani
|
||||
carlos
|
||||
dhowett
|
||||
Diviness
|
||||
dsafa
|
||||
duhowett
|
||||
ethanschoonover
|
||||
Firefox
|
||||
Gatta
|
||||
Grie
|
||||
Griese
|
||||
Hernan
|
||||
Howett
|
||||
Illhardt
|
||||
jantari
|
||||
jerrysh
|
||||
Kaiyu
|
||||
kimwalisch
|
||||
KMehrain
|
||||
Kourosh
|
||||
kowalczyk
|
||||
leonmsft
|
||||
Lepilleur
|
||||
Manandhar
|
||||
mbadolato
|
||||
Mehrain
|
||||
mgravell
|
||||
michaelniksa
|
||||
migrie
|
||||
mikegr
|
||||
mikemaccana
|
||||
miniksa
|
||||
niksa
|
||||
oising
|
||||
oldnewthing
|
||||
osgwiki
|
||||
paulcam
|
||||
pauldotknopf
|
||||
PGP
|
||||
Pham
|
||||
Rincewind
|
||||
rprichard
|
||||
Schoonover
|
||||
Somuah
|
||||
sonph
|
||||
sonpham
|
||||
stakx
|
||||
Walisch
|
||||
Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
Zamor
|
||||
Zamora
|
||||
zamora
|
||||
Zoey
|
||||
zorio
|
||||
Zverovich
|
||||
63
.github/actions/spell-check/excludes.txt
vendored
@@ -1,63 +0,0 @@
|
||||
(?:^|/)dirs$
|
||||
(?:^|/)go\.mod$
|
||||
(?:^|/)go\.sum$
|
||||
(?:^|/)package-lock\.json$
|
||||
(?:^|/)sources(?:|\.dep)$
|
||||
SUMS$
|
||||
\.ai$
|
||||
\.bmp$
|
||||
\.cer$
|
||||
\.class$
|
||||
\.crl$
|
||||
\.crt$
|
||||
\.csr$
|
||||
\.dll$
|
||||
\.DS_Store$
|
||||
\.eot$
|
||||
\.eps$
|
||||
\.exe$
|
||||
\.gif$
|
||||
\.graffle$
|
||||
\.gz$
|
||||
\.icns$
|
||||
\.ico$
|
||||
\.jar$
|
||||
\.jpeg$
|
||||
\.jpg$
|
||||
\.key$
|
||||
\.lib$
|
||||
\.lock$
|
||||
\.map$
|
||||
\.min\..
|
||||
\.mp3$
|
||||
\.mp4$
|
||||
\.otf$
|
||||
\.pbxproj$
|
||||
\.pdf$
|
||||
\.pem$
|
||||
\.png$
|
||||
\.psd$
|
||||
\.runsettings$
|
||||
\.sig$
|
||||
\.so$
|
||||
\.svg$
|
||||
\.svgz$
|
||||
\.tar$
|
||||
\.tgz$
|
||||
\.ttf$
|
||||
\.woff
|
||||
\.xcf$
|
||||
\.xls
|
||||
\.xpm$
|
||||
\.yml$
|
||||
\.zip$
|
||||
^consolegit2gitfilters\.json$
|
||||
^dep/
|
||||
^oss/
|
||||
^doc/reference/UTF8-torture-test\.txt$
|
||||
^src/interactivity/onecore/BgfxEngine\.
|
||||
^src/renderer/wddmcon/WddmConRenderer\.
|
||||
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^\.github/actions/spell-check/
|
||||
^\.gitignore$
|
||||
13
.github/actions/spell-check/expect/README.md
vendored
@@ -1,13 +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
|
||||
* [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).
|
||||
29
.github/actions/spell-check/expect/alphabet.txt
vendored
@@ -1,29 +0,0 @@
|
||||
abcde
|
||||
abcdef
|
||||
ABCDEFG
|
||||
ABCDEFGH
|
||||
ABCDEFGHIJ
|
||||
abcdefghijk
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
ABE
|
||||
BBBBBBBBBBBBBBDDDD
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
|
||||
qrstuvwxyz
|
||||
qwerty
|
||||
QWERTYUIOP
|
||||
qwertyuiopasdfg
|
||||
YYYYYYYDDDDDDDDDDD
|
||||
ZAAZZ
|
||||
ZABBZ
|
||||
ZBAZZ
|
||||
ZBBBZ
|
||||
ZBBZZ
|
||||
ZYXWVUT
|
||||
ZZBBZ
|
||||
ZZZBB
|
||||
ZZZBZ
|
||||
2808
.github/actions/spell-check/expect/expect.txt
vendored
15
.github/actions/spell-check/expect/web.txt
vendored
@@ -1,15 +0,0 @@
|
||||
http
|
||||
td
|
||||
www
|
||||
ecma
|
||||
rapidtables
|
||||
WCAG
|
||||
freedesktop
|
||||
ycombinator
|
||||
robertelder
|
||||
kovidgoyal
|
||||
leonerd
|
||||
fixterms
|
||||
uk
|
||||
winui
|
||||
appshellintegration
|
||||
2
.github/actions/spell-check/patterns/0_n.txt
vendored
@@ -1,2 +0,0 @@
|
||||
\\native(?![a-z])
|
||||
\\nihilist(?![a-z])
|
||||
8
.github/actions/spell-check/patterns/0_r.txt
vendored
@@ -1,8 +0,0 @@
|
||||
\\registry(?![a-z])
|
||||
\\release(?![a-z])
|
||||
\\resources?(?![a-z])
|
||||
\\result(?![a-z])
|
||||
\\resultmacros(?![a-z])
|
||||
\\rules(?![a-z])
|
||||
\\renderer(?![a-z])
|
||||
\\rectread(?![a-z])
|
||||
13
.github/actions/spell-check/patterns/0_t.txt
vendored
@@ -1,13 +0,0 @@
|
||||
\\telemetry(?![a-z])
|
||||
\\templates(?![a-z])
|
||||
\\term(?![a-z])
|
||||
\\terminal(?![a-z])
|
||||
\\terminalcore(?![a-z])
|
||||
\\terminalinput(?![a-z])
|
||||
\\testlist(?![a-z])
|
||||
\\testmd(?![a-z])
|
||||
\\testpasses(?![a-z])
|
||||
\\tests(?![a-z])
|
||||
\\thread(?![a-z])
|
||||
\\tools(?![a-z])
|
||||
\\types(?![a-z])
|
||||
16
.github/actions/spell-check/patterns/README.md
vendored
@@ -1,16 +0,0 @@
|
||||
The contents of each `.txt` file in this directory are merged together.
|
||||
Each line is a Perl 5 regular expression.
|
||||
Nothing is guaranteed about the order in which they're merged.
|
||||
-- If this is a problem, please reach out.
|
||||
|
||||
Note: order of the contents of these files can matter.
|
||||
Lines from an individual file are handled in file order.
|
||||
Files are selected in alphabetical order.
|
||||
|
||||
* [n](0_n.txt), [r](0_r.txt), and [t](0_t.txt) are specifically to work around
|
||||
a quirk in the spell checker:
|
||||
it often sees C strings of the form "Hello\nwerld". And would prefer to
|
||||
spot the typo of `werld`.
|
||||
* [patterns](patterns.txt) is the main list -- there is nothing
|
||||
particularly special about the file name (beyond the extension which is
|
||||
important).
|
||||
@@ -1,18 +0,0 @@
|
||||
https://(?:(?:[-a-zA-Z0-9?&=]*\.|)microsoft\.com)/[-a-zA-Z0-9?&=_#\/.]*
|
||||
https://aka\.ms/[-a-zA-Z0-9?&=\/_]*
|
||||
https://www\.itscj\.ipsj\.or\.jp/iso-ir/[-0-9]+\.pdf
|
||||
https://www\.vt100\.net/docs/[-a-zA-Z0-9#_\/.]*
|
||||
https://www.w3.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
https://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]*
|
||||
https://[a-z-]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
[Pp]ublicKeyToken="?[0-9a-fA-F]{16}"?
|
||||
(?:[{"]|UniqueIdentifier>)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|</UniqueIdentifier)
|
||||
(?:0[Xx]|\\x|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]{0,2}\b
|
||||
microsoft/cascadia-code\@[0-9a-fA-F]{40}
|
||||
\d+x\d+Logo
|
||||
Scro\&ll
|
||||
# selectionInput.cpp
|
||||
:\\windows\\syste\b
|
||||
TestUtils::VerifyExpectedString\(tb, L"[^"]+"
|
||||
hostSm\.ProcessString\(L"[^"]+"
|
||||
\b([A-Za-z])\1{3,}\b
|
||||
20
.github/workflows/spelling.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Spell checking
|
||||
on:
|
||||
push:
|
||||
schedule:
|
||||
# * is a special character in YAML so you have to quote this string
|
||||
- cron: '15 * * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Spell checking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.0.0
|
||||
with:
|
||||
fetch-depth: 5
|
||||
- uses: check-spelling/check-spelling@0.0.16-alpha
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
bucket: .github/actions
|
||||
project: spell-check
|
||||
10
.gitignore
vendored
@@ -48,7 +48,6 @@ dlldata.c
|
||||
project.lock.json
|
||||
artifacts/
|
||||
|
||||
*_h.h
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
@@ -145,13 +144,13 @@ publish/
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to check in your web deploy settings
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# check in your Azure Web App publish settings, but sensitive information contained
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
@@ -163,7 +162,7 @@ PublishScripts/
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
@@ -262,9 +261,6 @@ build*.rec
|
||||
build*.wrn
|
||||
build*.metadata
|
||||
|
||||
# MS Build binary logs
|
||||
*.binlog
|
||||
|
||||
# .razzlerc.cmd file - used by dev environment
|
||||
tools/.razzlerc.*
|
||||
# .PowershellModules - if one needs a powershell module dependency, one
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
|
||||
"Microsoft.VisualStudio.Component.VC.v142.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.v142.ARM64",
|
||||
"Microsoft.VisualStudio.Component.VC.v141.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.v141.ARM64",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v141",
|
||||
"Microsoft.VisualStudio.Component.UWP.VC.ARM64"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
# Microsoft Open Source Code of Conduct
|
||||
# Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code].
|
||||
For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments.
|
||||
|
||||
Resources:
|
||||
|
||||
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
||||
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
||||
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
|
||||
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
|
||||
[conduct-email]: mailto:opencode@microsoft.com
|
||||
|
||||
171
NOTICE.md
@@ -47,174 +47,3 @@ 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.
|
||||
```
|
||||
|
||||
## telnetpp
|
||||
|
||||
**Source**: https://github.com/KazDragon/telnetpp
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2017 Matthew Chaplain a.k.a KazDragon
|
||||
|
||||
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.
|
||||
```
|
||||
|
||||
## chromium/base/numerics
|
||||
|
||||
**Source**: https://github.com/chromium/chromium/tree/master/base/numerics
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
```
|
||||
|
||||
## kimwalisch/libpopcnt
|
||||
|
||||
**Source**: https://github.com/kimwalisch/libpopcnt
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2016 - 2019, Kim Walisch
|
||||
Copyright (c) 2016 - 2019, Wojciech Muła
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
|
||||
## dynamic_bitset
|
||||
|
||||
**Source**: https://github.com/pinam45/dynamic_bitset
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Maxime Pinard
|
||||
|
||||
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.
|
||||
|
||||
```
|
||||
|
||||
## {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.
|
||||
```
|
||||
|
||||
1262
OpenConsole.sln
228
README.md
@@ -1,182 +1,131 @@
|
||||
# Welcome to the Windows Terminal, Console and Command-Line repo
|
||||
# Welcome\!
|
||||
#### This repository contains the source code for:
|
||||
|
||||
This repository contains the source code for:
|
||||
* Windows Terminal
|
||||
* The Windows console host (`conhost.exe`)
|
||||
* Components shared between the two projects
|
||||
* [ColorTool](https://github.com/Microsoft/Terminal/tree/master/src/tools/ColorTool)
|
||||
* [Sample projects](https://github.com/Microsoft/Terminal/tree/master/samples) that show how to consume the Windows Console APIs
|
||||
|
||||
#### Other related repositories include:
|
||||
* [Console API Documentation](https://github.com/MicrosoftDocs/Console-Docs)
|
||||
|
||||
* [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)
|
||||
* [Sample projects](https://github.com/Microsoft/Terminal/tree/master/samples) that show how to consume the Windows Console APIs
|
||||
|
||||
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)
|
||||
|
||||
## Installing and running Windows Terminal
|
||||
|
||||
> 👉 Note: Windows Terminal requires Windows 10 1903 (build 18362) or later
|
||||
|
||||
### 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.
|
||||
|
||||
This is our preferred method.
|
||||
|
||||
### Other install methods
|
||||
|
||||
#### Via GitHub
|
||||
|
||||
For users who are unable to install Terminal from the Microsoft Store, Terminal builds can be manually downloaded from this repository's [Releases page](https://github.com/microsoft/terminal/releases).
|
||||
|
||||
> ⚠ Note: If you install Terminal manually:
|
||||
>
|
||||
> * 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:
|
||||
|
||||
```powershell
|
||||
choco install microsoft-windows-terminal
|
||||
```
|
||||
|
||||
To upgrade Windows Terminal using Chocolatey, run the following:
|
||||
|
||||
```powershell
|
||||
choco upgrade microsoft-windows-terminal
|
||||
```
|
||||
|
||||
If you have any issues when installing/upgrading the package please go to the [Windows Terminal package page](https://chocolatey.org/packages/microsoft-windows-terminal) and follow the [Chocolatey triage process](https://chocolatey.org/docs/package-triage-process)
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
### Build Status
|
||||
|
||||
Project|Build Status
|
||||
---|---
|
||||
Terminal|[](https://dev.azure.com/ms/Terminal/_build?definitionId=136)
|
||||
ColorTool|
|
||||
|
||||
---
|
||||
|
||||
## Terminal & Console Overview
|
||||
# Terminal & Console Overview
|
||||
|
||||
Please take a few minutes to review the overview below before diving into the code:
|
||||
|
||||
### Windows Terminal
|
||||
## Windows Terminal
|
||||
|
||||
Windows Terminal is a new, modern, feature-rich, productive terminal application for command-line users. It includes many of the features most frequently requested by the Windows command-line community including support for tabs, rich text, globalization, configurability, theming & styling, and more.
|
||||
|
||||
The Terminal will also need to meet our goals and measures to ensure it remains fast and efficient, and doesn't consume vast amounts of memory or power.
|
||||
The Terminal will also need to meet our goals and measures to ensure it remains fast, and efficient, and doesn't consume vast amounts of memory or power.
|
||||
|
||||
### The Windows Console Host
|
||||
## The Windows console host
|
||||
|
||||
The Windows Console host, `conhost.exe`, is Windows' original command-line user experience. It also hosts Windows' command-line infrastructure and the Windows Console API server, input engine, rendering engine, user preferences, etc. The console host code in this repository is the actual source from which the `conhost.exe` in Windows itself is built.
|
||||
The Windows console host, `conhost.exe`, is Windows' original command-line user experience. It implements Windows' command-line infrastructure, and is responsible for hosting the Windows Console API, input engine, rendering engine, and user preferences. The console host code in this repository is the actual source from which the `conhost.exe` in Windows itself is built.
|
||||
|
||||
Since taking ownership of the Windows command-line in 2014, the team added several new features to the Console, including background transparency, line-based selection, support for [ANSI / Virtual Terminal sequences](https://en.wikipedia.org/wiki/ANSI_escape_code), [24-bit color](https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/), a [Pseudoconsole ("ConPTY")](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/), and more.
|
||||
Console's primary goal is to remain backwards-compatible with existing console subsystem applications.
|
||||
|
||||
However, because Windows Console's primary goal is to maintain backward compatibility, we have been unable to add many of the features the community (and the team) have been wanting for the last several years including tabs, unicode text, and emoji.
|
||||
Since assuming ownership of the Windows command-line in 2014, the team has added several new features to the Console, including window transparency, line-based selection, support for [ANSI / Virtual Terminal sequences](https://en.wikipedia.org/wiki/ANSI_escape_code), [24-bit color](https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/), a [Pseudoconsole ("ConPTY")](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/), and more.
|
||||
|
||||
However, because the Console's primary goal is to maintain backward compatibility, we've been unable to add many of the features the community has been asking for, and which we've been wanting to add for the last several years--like tabs!
|
||||
|
||||
These limitations led us to create the new Windows Terminal.
|
||||
|
||||
> You can read more about the evolution of the command-line in general, and the Windows command-line specifically in [this accompanying series of blog posts](https://devblogs.microsoft.com/commandline/windows-command-line-backgrounder/) on the Command-Line team's blog.
|
||||
## Shared Components
|
||||
|
||||
### Shared Components
|
||||
While overhauling the Console, we've modernized its codebase considerably. We've cleanly separated logical entities into modules and classes, introduced some key extensibility points, replaced several old, home-grown collections and containers with safer, more efficient [STL containers](https://docs.microsoft.com/en-us/cpp/standard-library/stl-containers?view=vs-2019), and made the code simpler and safer by using Microsoft's [WIL](https://github.com/Microsoft/wil) header library.
|
||||
|
||||
While overhauling Windows Console, we modernized its codebase considerably, cleanly separating logical entities into modules and classes, introduced some key extensibility points, replaced several old, home-grown collections and containers with safer, more efficient [STL containers](https://docs.microsoft.com/en-us/cpp/standard-library/stl-containers?view=vs-2019), and made the code simpler and safer by using Microsoft's [Windows Implementation Libraries - WIL](https://github.com/Microsoft/wil).
|
||||
This overhaul work resulted in the creation of several key components that would be useful for any terminal implementation on Windows, including a new DirectWrite-based text layout and rendering engine, a text buffer capable of storing both UTF-16 and UTF-8, and a VT parser/emitter.
|
||||
|
||||
This overhaul resulted in several of Console's key components being available for re-use in any terminal implementation on Windows. These components include a new DirectWrite-based text layout and rendering engine, a text buffer capable of storing both UTF-16 and UTF-8, a VT parser/emitter, and more.
|
||||
## Building a new terminal
|
||||
|
||||
### Creating the new Windows Terminal
|
||||
When we started building the new terminal application, we explored and evaluated several approaches and technology stacks. We ultimately decided that our goals would be best met by sticking with C++ and sharing the aforementioned modernized components, placing them atop the modern Windows application platform and UI framework.
|
||||
|
||||
When we started planning the new Windows Terminal application, we explored and evaluated several approaches and technology stacks. We ultimately decided that our goals would be best met by continuing our investment in our C++ codebase, which would allow us to reuse several of the aforementioned modernized components in both the existing Console and the new Terminal. Further, we realized that this would allow us to build much of the Terminal's core itself as a reusable UI control that others can incorporate into their own applications.
|
||||
Further, we realized that this would allow us to build the terminal's renderer and input stack as a reusable Windows UI control that others can incorporate into their applications.
|
||||
|
||||
The result of this work is contained within this repo and delivered as the Windows Terminal application you can download from the Microsoft Store, or [directly from this repo's releases](https://github.com/microsoft/terminal/releases).
|
||||
# FAQ
|
||||
|
||||
---
|
||||
## Where can I download Windows Terminal?
|
||||
|
||||
## Resources
|
||||
### The Windows Terminal preview can be downloaded from the Microsoft Store.
|
||||
|
||||
For more information about Windows Terminal, you may find some of these resources useful and interesting:
|
||||
[https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701](https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701)
|
||||
|
||||
* [Command-Line Blog](https://devblogs.microsoft.com/commandline)
|
||||
* [Command-Line Backgrounder Blog Series](https://devblogs.microsoft.com/commandline/windows-command-line-backgrounder/)
|
||||
* Windows Terminal Launch: [Terminal "Sizzle Video"](https://www.youtube.com/watch?v=8gw0rXPMMPE&list=PLEHMQNlPj-Jzh9DkNpqipDGCZZuOwrQwR&index=2&t=0s)
|
||||
* Windows Terminal Launch: [Build 2019 Session](https://www.youtube.com/watch?v=KMudkRcwjCw)
|
||||
* Run As Radio: [Show 645 - Windows Terminal with Richard Turner](http://www.runasradio.com/Shows/Show/645)
|
||||
* Azure Devops Podcast: [Episode 54 - Kayla Cinnamon and Rich Turner on DevOps on the Windows Terminal](http://azuredevopspodcast.clear-measure.com/kayla-cinnamon-and-rich-turner-on-devops-on-the-windows-terminal-team-episode-54)
|
||||
* Microsoft Ignite 2019 Session: [The Modern Windows Command Line: Windows Terminal - BRK3321](https://myignite.techcommunity.microsoft.com/sessions/81329?source=sessions)
|
||||
## I built and ran the new Terminal, but I just get a blank window app!
|
||||
|
||||
---
|
||||
Make sure you are building for your computer's architecture. If your box has a 64-bit Windows, change your Solution Platform to x64.
|
||||
To check your OS architecture go to Settings -> System -> About (or Win+X -> System) and under `Device specifications` check for the `System type`.
|
||||
|
||||
## FAQ
|
||||
## I built and ran the new Terminal, but it looks just like the old console! What gives?
|
||||
|
||||
### I built and ran the new Terminal, but it looks just like the old console
|
||||
Firstly, make sure you're building & deploying `CascadiaPackage` in Visual Studio, _NOT_ `Host.EXE`. `OpenConsole.exe` is just `conhost.exe`, the same old console you know and love. `opencon.cmd` will launch `openconsole.exe`, and unfortunately, `openterm.cmd` is currently broken.
|
||||
|
||||
Cause: You're launching the incorrect solution in Visual Studio.
|
||||
Secondly, try pressing <kbd>Ctrl</kbd> + <kbd>T</kbd>. The tabs are hidden when you only have one tab by default. In the future, the UI will be dramatically different, but for now, the defaults are _supposed_ to look like the console defaults.
|
||||
|
||||
Solution: Make sure you're building & deploying the `CascadiaPackage` project in Visual Studio.
|
||||
## I tried running WindowsTerminal.exe and it crashes!
|
||||
|
||||
> ⚠ Note: `OpenConsole.exe` is just a locally-built `conhost.exe`, the classic Windows Console that hosts Windows' command-line infrastructure. OpenConsole is used by Windows Terminal to connect to and communicate with command-line applications (via [ConPty](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/)).
|
||||
* Don't try to run it unpackaged. Make sure to build & deploy `CascadiaPackage` from Visual Studio, and run the Windows Terminal (Dev Build) app.
|
||||
* Make sure you're on the right version of Windows. You'll need to be on Insider's builds, or wait for the 1903 release, as the Windows Terminal **REQUIRES** features from the latest Windows release.
|
||||
|
||||
---
|
||||
# Getting Started
|
||||
|
||||
## Documentation
|
||||
## Debugging
|
||||
|
||||
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).
|
||||
|
||||
---
|
||||
* To debug in VS, right click on CascadiaPackage (from VS Solution Explorer) and go to properties, in the Debug menu, change "Application process" and "Background task process" to "Native Only".
|
||||
|
||||
## Contributing
|
||||
|
||||
We are excited to work alongside you, our amazing community, to build and enhance Windows Terminal\!
|
||||
|
||||
***BEFORE you start work on a feature/fix***, please read & follow our [Contributor's Guide](https://github.com/microsoft/terminal/blob/master/CONTRIBUTING.md) to help avoid any wasted or duplicate effort.
|
||||
We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](https://github.com/microsoft/terminal/blob/master/doc/contributing.md). We will be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort.
|
||||
|
||||
> 👉 **Remember\!** Your contributions may be incorporated into future versions of Windows\! Because of this, all pull requests will be subject to the same level of scrutiny for quality, coding standards, performance, globalization, accessibility, and compatibility as those of our internal contributors.
|
||||
|
||||
> ⚠ **Note**: The Command-Line Team is actively working out of this repository and will be periodically re-structuring the code to make it easier to comprehend, navigate, build, test, and contribute to, so **DO expect significant changes to code layout on a regular basis**.
|
||||
|
||||
## Documentation
|
||||
|
||||
All documentation is located in the `./doc` folder. If you would like to contribute to the documentation, please submit a pull request.
|
||||
|
||||
## Communicating with the Team
|
||||
|
||||
The easiest way to communicate with the team is via GitHub issues.
|
||||
The easiest way to communicate with the team is via GitHub issues. Please file new issues, feature requests and suggestions, but **DO search for similar open/closed pre-existing issues before you do**.
|
||||
|
||||
Please file new issues, feature requests and suggestions, but **DO search for similar open/closed pre-existing issues before creating a new issue.**
|
||||
Please help us keep this repository clean, inclusive, and fun\! We will not tolerate any abusive, rude, disrespectful or inappropriate behavior. Read our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/) for more details.
|
||||
|
||||
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)
|
||||
* 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)
|
||||
* Carlos Zamora, Developer: [@cazamor_msft](https://twitter.com/cazamor_msft)
|
||||
* Leon Liang, Developer: [@leonmsft](https://twitter.com/leonmsft)
|
||||
* Rich Turner, Program Manager: [@richturn\_ms](https://twitter.com/richturn_ms)
|
||||
|
||||
## Developer Guidance
|
||||
* Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett)
|
||||
|
||||
* Michael Niksa, Senior Developer: [@michaelniksa](https://twitter.com/MichaelNiksa)
|
||||
|
||||
## Prerequisites
|
||||
* Kayla Cinnamon, Program Manager (especially for UX issues): [@cinnamon\_msft](https://twitter.com/cinnamon_msft)
|
||||
|
||||
# Developer Guidance
|
||||
|
||||
## Build Prerequisites
|
||||
|
||||
* You must be running Windows 1903 (build >= 10.0.18362.0) or above in order to run Windows Terminal.
|
||||
* You must have the [1903 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) (build 10.0.18362.0) installed.
|
||||
* You must have at least [VS 2019](https://visualstudio.microsoft.com/downloads/) installed.
|
||||
* You must install the following Workloads via the VS Installer. Opening the solution will [prompt you to install missing components automatically](https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/).
|
||||
- Desktop Development with C++
|
||||
- Universal Windows Platform Development
|
||||
- **The following Individual Components**
|
||||
- C++ (v142) Universal Windows Platform Tools
|
||||
|
||||
* You must also [enable Developer Mode in the Windows Settings app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) to locally install and run the Terminal app.
|
||||
|
||||
* You must be running Windows 1903 (build >= 10.0.18362.0) or later to run Windows Terminal
|
||||
* You must [enable Developer Mode in the Windows Settings app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) to locally install and run Windows Terminal
|
||||
* You must have the [Windows 10 1903 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) installed
|
||||
* You must have at least [VS 2019](https://visualstudio.microsoft.com/downloads/) installed
|
||||
* You must install the following Workloads via the VS Installer. Note: Opening the solution in VS 2019 will [prompt you to install missing components automatically](https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/):
|
||||
* Desktop Development with C++
|
||||
* Universal Windows Platform Development
|
||||
* **The following Individual Components**
|
||||
* C++ (v142) Universal Windows Platform Tools
|
||||
|
||||
## Building the Code
|
||||
|
||||
@@ -186,9 +135,9 @@ This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-S
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory:
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command-line using MSBuild. To build from the command line, find your shell below.
|
||||
|
||||
### Building in PowerShell
|
||||
### PowerShell
|
||||
|
||||
```powershell
|
||||
Import-Module .\tools\OpenConsole.psm1
|
||||
@@ -196,35 +145,27 @@ Set-MsBuildDevEnvironment
|
||||
Invoke-OpenConsoleBuild
|
||||
```
|
||||
|
||||
### Building in Cmd
|
||||
### CMD
|
||||
|
||||
```shell
|
||||
.\tools\razzle.cmd
|
||||
bcz
|
||||
```
|
||||
|
||||
## Running & Debugging
|
||||
We've provided a set of convenience scripts as well as [README](./tools/README.md) in the **/tools** directory to help automate the process of building and running tests.
|
||||
|
||||
To debug the Windows Terminal in VS, right click on `CascadiaPackage` (in the Solution Explorer) and go to properties. In the Debug menu, change "Application process" and "Background task process" to "Native Only".
|
||||
## Coding Guidance
|
||||
|
||||
You should then be able to build & debug the Terminal project by hitting <kbd>F5</kbd>.
|
||||
Please review these brief docs below relating to our coding standards etc.
|
||||
|
||||
> 👉 You will _not_ be able to launch the Terminal directly by running the WindowsTerminal.exe. For more details on why, see [#926](https://github.com/microsoft/terminal/issues/926), [#4043](https://github.com/microsoft/terminal/issues/4043)
|
||||
|
||||
### Coding Guidance
|
||||
|
||||
Please review these brief docs below about our coding practices.
|
||||
|
||||
> 👉 If you find something missing from these docs, feel free to contribute to any of our documentation files anywhere in the repository (or write some new ones!)
|
||||
> 👉 If you find something missing from these docs, feel free to contribute to any of our documentation files anywhere in the repository (or make some new ones\!)
|
||||
|
||||
This is a work in progress as we learn what we'll need to provide people in order to be effective contributors to our project.
|
||||
|
||||
* [Coding Style](https://github.com/Microsoft/Terminal/blob/master/doc/STYLE.md)
|
||||
* [Code Organization](https://github.com/Microsoft/Terminal/blob/master/doc/ORGANIZATION.md)
|
||||
* [Exceptions in our legacy codebase](https://github.com/Microsoft/Terminal/blob/master/doc/EXCEPTIONS.md)
|
||||
* [Helpful smart pointers and macros for interfacing with Windows in WIL](https://github.com/Microsoft/Terminal/blob/master/doc/WIL.md)
|
||||
|
||||
---
|
||||
- [Coding Style](https://github.com/Microsoft/Terminal/blob/master/doc/STYLE.md)
|
||||
- [Code Organization](https://github.com/Microsoft/Terminal/blob/master/doc/ORGANIZATION.md)
|
||||
- [Exceptions in our legacy codebase](https://github.com/Microsoft/Terminal/blob/master/doc/EXCEPTIONS.md)
|
||||
- [Helpful smart pointers and macros for interfacing with Windows in WIL](https://github.com/Microsoft/Terminal/blob/master/doc/WIL.md)
|
||||
|
||||
# Code of Conduct
|
||||
|
||||
@@ -234,4 +175,3 @@ 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/terminal
|
||||
|
||||
41
SECURITY.md
@@ -1,41 +0,0 @@
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.2 BLOCK -->
|
||||
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
4
build/.nuget/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Taef.TestAdapter" version="10.30.180808002" />
|
||||
</packages>
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Modules>
|
||||
<Module name="Microsoft.WindowsTerminal" tdbuildteamid="7105">
|
||||
<File location="TerminalApp"
|
||||
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\TerminalApp\Resources\en-US\Resources.resw" />
|
||||
<File location="TerminalControl"
|
||||
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\TerminalControl\Resources\en-US\Resources.resw" />
|
||||
<File location="TerminalConnection"
|
||||
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\TerminalConnection\Resources\en-US\Resources.resw" />
|
||||
<File location="WindowsTerminalUniversal"
|
||||
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\WindowsTerminalUniversal\Resources\en-US\Resources.resw" />
|
||||
<File location="CascadiaPackage"
|
||||
path="%BUILD_SOURCESDIRECTORY%\src\cascadia\CascadiaPackage\Resources\en-US\Resources.resw" />
|
||||
</Module>
|
||||
</Modules>
|
||||
9
build/config/NuGet.config
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="TAEF Internal" value="https://microsoft.pkgs.visualstudio.com/_packaging/Taef/nuget/v3/index.json" />
|
||||
</packageSources>
|
||||
<config>
|
||||
<add key="repositorypath" value="..\..\packages" />
|
||||
</config>
|
||||
</configuration>
|
||||
@@ -1,5 +0,0 @@
|
||||
<SignConfigXML>
|
||||
<job platform="" configuration="" dest="__INPATHROOT__" jobname="EngFunSimpleSign" approvers="">
|
||||
<file src="__INPATHROOT__\Microsoft.Terminal*.nupkg" signType="NuGet" />
|
||||
</job>
|
||||
</SignConfigXML>
|
||||
@@ -1,5 +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*.msixbundle" signType="136020001" />
|
||||
<file src="__INPATHROOT__\Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle" signType="136020001" dest="__OUTPATHROOT__\Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle" />
|
||||
</job>
|
||||
</SignConfigXML>
|
||||
|
||||
@@ -13,56 +13,26 @@ pr:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
paths:
|
||||
exclude:
|
||||
- doc/*
|
||||
- samples/*
|
||||
- tools/*
|
||||
|
||||
variables:
|
||||
- name: runCodesignValidationInjectionBG
|
||||
value: false
|
||||
|
||||
# 0.0.yyMM.dd##
|
||||
# 0.0.1904.0900
|
||||
name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
|
||||
|
||||
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
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
|
||||
- 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,14 +15,6 @@ 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:
|
||||
@@ -31,17 +23,17 @@ jobs:
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
additionalBuildArguments: /p:WindowsTerminalReleaseBuild=true
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
additionalBuildArguments: /p:WindowsTerminalReleaseBuild=true
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: arm64
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
additionalBuildArguments: /p:WindowsTerminalReleaseBuild=true
|
||||
|
||||
- template: ./templates/check-formatting.yml
|
||||
|
||||
|
||||
@@ -31,6 +31,16 @@ jobs:
|
||||
restoreSolution: OpenConsole.sln
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\packages'
|
||||
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: 'NuGet restore packages for CI'
|
||||
inputs:
|
||||
command: restore
|
||||
restoreSolution: build/.nuget/packages.config
|
||||
feedsToUse: config
|
||||
externalFeedCredentials: 'TAEF NuGet Feed'
|
||||
nugetConfigPath: build/config/NuGet.config
|
||||
restoreDirectory: '$(Build.SourcesDirectory)/packages'
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
|
||||
@@ -25,6 +25,16 @@ steps:
|
||||
restoreSolution: OpenConsole.sln
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\packages'
|
||||
|
||||
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
|
||||
displayName: 'NuGet restore packages for CI'
|
||||
inputs:
|
||||
command: restore
|
||||
restoreSolution: build/.nuget/packages.config
|
||||
feedsToUse: config
|
||||
externalFeedCredentials: 'TAEF NuGet Feed'
|
||||
nugetConfigPath: build/config/NuGet.config
|
||||
restoreDirectory: '$(Build.SourcesDirectory)/packages'
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
@@ -32,49 +42,36 @@ steps:
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }}"
|
||||
msbuildArgs: ${{ parameters.additionalBuildArguments }}
|
||||
clean: true
|
||||
maximumCpuCount: true
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Check MSIX for common regressions'
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
$Package = Get-ChildItem -Recurse -Filter "CascadiaPackage_*.msix"
|
||||
.\build\scripts\Test-WindowsTerminalPackage.ps1 -Verbose -Path $Package.FullName
|
||||
|
||||
- task: powershell@2
|
||||
displayName: 'Source Index PDBs'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\scripts\Index-Pdbs.ps1
|
||||
arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
|
||||
errorActionPreference: silentlyContinue
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Rationalize build platform'
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
$Arch = "$(BuildPlatform)"
|
||||
If ($Arch -Eq "x86") { $Arch = "Win32" }
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
|
||||
- task: PowerShell@2
|
||||
- task: VSTest@2
|
||||
displayName: 'Run Unit Tests'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*unit.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
|
||||
testAssemblyVer2: |
|
||||
$(BUILD.SOURCESDIRECTORY)\**\*unit.test*.dll
|
||||
!**\obj\**
|
||||
runSettingsFile: '$(BUILD.SOURCESDIRECTORY)\src\unit.tests.$(BuildPlatform).runsettings'
|
||||
codeCoverageEnabled: true
|
||||
runInParallel: False
|
||||
testRunTitle: 'Console Unit Tests'
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
|
||||
|
||||
- task: PowerShell@2
|
||||
- task: VSTest@2
|
||||
displayName: 'Run Feature Tests (x64 only)'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
|
||||
testAssemblyVer2: |
|
||||
$(BUILD.SOURCESDIRECTORY)\**\*feature.test*.dll
|
||||
!**\obj\**
|
||||
runSettingsFile: '$(BUILD.SOURCESDIRECTORY)\src\unit.tests.$(BuildPlatform).runsettings'
|
||||
codeCoverageEnabled: true
|
||||
runInParallel: False
|
||||
testRunTitle: 'Console Feature Tests'
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
|
||||
- task: CopyFiles@2
|
||||
|
||||
@@ -6,7 +6,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
submodules: false
|
||||
clean: true
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<BeforeLinkTargets Condition="'$(WindowsTargetPlatformVersion)' >= '10.0.18362.0'">
|
||||
$(BeforeLinkTargets);
|
||||
_ConsoleGenerateAdditionalWinmdManifests;
|
||||
</BeforeLinkTargets>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="_ConsoleMapWinmdsToManifestFiles" DependsOnTargets="ResolveAssemblyReferences">
|
||||
<ItemGroup>
|
||||
<!-- For each non-system .winmd file in References, generate a .manifest in IntDir for it. -->
|
||||
<_ConsoleWinmdManifest Include="@(ReferencePath->'$(IntDir)\%(FileName).manifest')" Condition="'%(ReferencePath.IsSystemReference)' != 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference' and '%(ReferencePath.Implementation)' != ''">
|
||||
<WinMDPath>%(ReferencePath.FullPath)</WinMDPath>
|
||||
<Implementation>%(ReferencePath.Implementation)</Implementation>
|
||||
</_ConsoleWinmdManifest>
|
||||
<!-- For each referenced project that _produces_ a winmd, generate a temporary item that maps to
|
||||
the winmd, and use that temporary item to generate a .manifest in IntDir for it.
|
||||
We don't set Implementation here because it's inherited from the _ResolvedNativeProjectReferencePaths. -->
|
||||
<_ConsoleWinmdProjectReference Condition="'%(_ResolvedNativeProjectReferencePaths.ProjectType)' != 'StaticLibrary'" Include="@(_ResolvedNativeProjectReferencePaths->WithMetadataValue('FileType','winmd')->'%(RootDir)%(Directory)%(TargetPath)')" />
|
||||
<_ConsoleWinmdManifest Include="@(_ConsoleWinmdProjectReference->'$(IntDir)\%(FileName).manifest')">
|
||||
<WinMDPath>%(Identity)</WinMDPath>
|
||||
</_ConsoleWinmdManifest>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_ConsoleGenerateAdditionalWinmdManifests"
|
||||
Inputs="@(_ConsoleWinmdManifest.WinMDPath)"
|
||||
Outputs="@(_ConsoleWinmdManifest)"
|
||||
DependsOnTargets="_ConsoleMapWinmdsToManifestFiles">
|
||||
|
||||
<!-- This target is batched and a new Exec is spawned for each entry in _ConsoleWinmdManifest. -->
|
||||
<Exec Command="mt.exe -winmd:%(_ConsoleWinmdManifest.WinMDPath) -dll:%(_ConsoleWinmdManifest.Implementation) -out:%(_ConsoleWinmdManifest.Identity)" />
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Emit the generated manifest into the Link inputs. -->
|
||||
<Manifest Include="@(_ConsoleWinmdManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,14 +0,0 @@
|
||||
[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
|
||||
@@ -1,85 +0,0 @@
|
||||
[CmdLetBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true, Position=0)][string]$SearchDir,
|
||||
[Parameter(Mandatory=$true, Position=1)][string]$SourceRoot,
|
||||
[Parameter(Mandatory=$true, Position=2)][string]$CommitId,
|
||||
[string]$Organization = "microsoft",
|
||||
[string]$Repo = "terminal",
|
||||
[switch]$recursive
|
||||
)
|
||||
|
||||
$debuggerPath = (Get-ItemProperty -path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots" -name WindowsDebuggersRoot10).WindowsDebuggersRoot10
|
||||
$srcsrvPath = Join-Path $debuggerPath "x64\srcsrv"
|
||||
$srctoolExe = Join-Path $srcsrvPath "srctool.exe"
|
||||
$pdbstrExe = Join-Path $srcsrvPath "pdbstr.exe"
|
||||
|
||||
$fileTable = @{}
|
||||
foreach ($gitFile in & git ls-files)
|
||||
{
|
||||
$fileTable[$gitFile] = $gitFile
|
||||
}
|
||||
|
||||
$mappedFiles = New-Object System.Collections.ArrayList
|
||||
|
||||
foreach ($file in (Get-ChildItem -r:$recursive "$SearchDir\*.pdb"))
|
||||
{
|
||||
Write-Verbose "Found $file"
|
||||
|
||||
$ErrorActionPreference = "Continue" # Azure Pipelines defaults to "Stop", continue past errors in this script.
|
||||
|
||||
$allFiles = & $srctoolExe -r "$file"
|
||||
|
||||
# If the pdb didn't have enough files then skip it (the srctool output has a blank line even when there's no info
|
||||
# so check for less than 2 lines)
|
||||
if ($allFiles.Length -lt 2)
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
for ($i = 0; $i -lt $allFiles.Length; $i++)
|
||||
{
|
||||
if ($allFiles[$i].StartsWith($SourceRoot, [StringComparison]::OrdinalIgnoreCase))
|
||||
{
|
||||
$relative = $allFiles[$i].Substring($SourceRoot.Length).TrimStart("\")
|
||||
$relative = $relative.Replace("\", "/")
|
||||
|
||||
# Git urls are case-sensitive but the PDB might contain a lowercased version of the file path.
|
||||
# Look up the relative url in the output of "ls-files". If it's not there then it's not something
|
||||
# in git, so don't index it.
|
||||
$relative = $fileTable[$relative]
|
||||
if ($relative)
|
||||
{
|
||||
$mapping = $allFiles[$i] + "*$relative"
|
||||
$mappedFiles.Add($mapping)
|
||||
|
||||
Write-Verbose "Mapped path $($i): $mapping"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pdbstrFile = Join-Path "$env:TEMP" "pdbstr.txt"
|
||||
|
||||
Write-Verbose "pdbstr.txt = $pdbstrFile"
|
||||
|
||||
@"
|
||||
SRCSRV: ini ------------------------------------------------
|
||||
VERSION=2
|
||||
VERCTRL=http
|
||||
SRCSRV: variables ------------------------------------------
|
||||
ORGANIZATION=$Organization
|
||||
REPO=$Repo
|
||||
COMMITID=$CommitId
|
||||
HTTP_ALIAS=https://raw.githubusercontent.com/%ORGANIZATION%/%REPO%/%COMMITID%/
|
||||
HTTP_EXTRACT_TARGET=%HTTP_ALIAS%%var2%
|
||||
SRCSRVTRG=%HTTP_EXTRACT_TARGET%
|
||||
SRC_INDEX=public
|
||||
SRCSRV: source files ---------------------------------------
|
||||
$($mappedFiles -join "`r`n")
|
||||
SRCSRV: end ------------------------------------------------
|
||||
"@ | Set-Content $pdbstrFile
|
||||
|
||||
& $pdbstrExe -p:"$file" -w -s:srcsrv -i:$pdbstrFile
|
||||
}
|
||||
|
||||
# Return with exit 0 to override any weird error code from other tools
|
||||
Exit 0
|
||||
@@ -1,14 +0,0 @@
|
||||
[CmdLetBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true, Position=0)][string]$MatchPattern,
|
||||
[Parameter(Mandatory=$true, Position=1)][string]$Platform,
|
||||
[Parameter(Mandatory=$true, Position=2)][string]$Configuration
|
||||
)
|
||||
|
||||
$testdlls = Get-ChildItem -Path ".\bin\$Platform\$Configuration" -Recurse -Filter $MatchPattern
|
||||
|
||||
&".\bin\$Platform\$Configuration\te.exe" $testdlls.FullName
|
||||
|
||||
if ($lastexitcode -Ne 0) { Exit $lastexitcode }
|
||||
|
||||
Exit 0
|
||||
@@ -1,106 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true, ValueFromPipeline=$true,
|
||||
HelpMessage="Path to the .appx/.msix to validate")]
|
||||
[string]
|
||||
$Path,
|
||||
|
||||
[Parameter(HelpMessage="Path to Windows Kit")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
If ($null -Eq (Get-Item $WindowsKitPath -EA:SilentlyContinue)) {
|
||||
Write-Error "Could not find a windows SDK at at `"$WindowsKitPath`".`nMake sure that WindowsKitPath points to a valid SDK."
|
||||
Exit 1
|
||||
}
|
||||
|
||||
$makeAppx = "$WindowsKitPath\x86\MakeAppx.exe"
|
||||
$makePri = "$WindowsKitPath\x86\MakePri.exe"
|
||||
|
||||
Function Expand-ApplicationPackage {
|
||||
Param(
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[string]
|
||||
$Path
|
||||
)
|
||||
|
||||
$sentinelFile = New-TemporaryFile
|
||||
$directory = New-Item -Type Directory "$($sentinelFile.FullName)_Package"
|
||||
Remove-Item $sentinelFile -Force -EA:Ignore
|
||||
|
||||
& $makeAppx unpack /p $Path /d $directory /nv /o
|
||||
|
||||
If ($LastExitCode -Ne 0) {
|
||||
Throw "Failed to expand AppX"
|
||||
}
|
||||
|
||||
$directory
|
||||
}
|
||||
|
||||
Write-Verbose "Expanding $Path"
|
||||
$AppxPackageRoot = Expand-ApplicationPackage $Path
|
||||
$AppxPackageRootPath = $AppxPackageRoot.FullName
|
||||
|
||||
Write-Verbose "Expanded to $AppxPackageRootPath"
|
||||
|
||||
Try {
|
||||
& $makePri dump /if "$AppxPackageRootPath\resources.pri" /of "$AppxPackageRootPath\resources.pri.xml" /o
|
||||
If ($LastExitCode -Ne 0) {
|
||||
Throw "Failed to dump PRI"
|
||||
}
|
||||
|
||||
$Manifest = [xml](Get-Content "$AppxPackageRootPath\AppxManifest.xml")
|
||||
$PRIFile = [xml](Get-Content "$AppxPackageRootPath\resources.pri.xml")
|
||||
|
||||
### Check the activatable class entries for a few DLLs we need.
|
||||
$inProcServers = $Manifest.Package.Extensions.Extension.InProcessServer.Path
|
||||
$RequiredInProcServers = ("TerminalApp.dll", "TerminalControl.dll", "TerminalConnection.dll")
|
||||
|
||||
Write-Verbose "InProc Servers: $inProcServers"
|
||||
|
||||
ForEach ($req in $RequiredInProcServers) {
|
||||
If ($req -NotIn $inProcServers) {
|
||||
Throw "Failed to find $req in InProcServer list $inProcServers"
|
||||
}
|
||||
}
|
||||
|
||||
$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)
|
||||
If ($null -eq $AppXbf) {
|
||||
Throw "Failed to find App.xbf (TerminalApp project) in resources.pri"
|
||||
}
|
||||
|
||||
If (($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10.dll" -EA:Ignore)) -And
|
||||
($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10d.dll" -EA:Ignore))) {
|
||||
Throw "Failed to find cpprest142_2_10.dll -- check the WAP packaging project"
|
||||
}
|
||||
|
||||
} Finally {
|
||||
Remove-Item -Recurse -Force $AppxPackageRootPath
|
||||
}
|
||||
@@ -14,14 +14,9 @@
|
||||
"/.vs/",
|
||||
"/build/",
|
||||
"/src/cascadia/",
|
||||
"/src/winconpty/",
|
||||
"/.nuget/",
|
||||
"/.github/",
|
||||
"/samples/",
|
||||
"/res/terminal/",
|
||||
"/doc/specs/",
|
||||
"/doc/cascadia/",
|
||||
"/doc/user-docs/"
|
||||
"/samples/"
|
||||
],
|
||||
"SuffixFilters": [
|
||||
".dbb",
|
||||
@@ -36,7 +31,6 @@
|
||||
".db",
|
||||
".wrn",
|
||||
".rec",
|
||||
".err",
|
||||
".xlsx"
|
||||
".err"
|
||||
]
|
||||
}
|
||||
|
||||
11
custom.props
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- This file is read by XES, which we use in our Release builds. -->
|
||||
<PropertyGroup Label="Version">
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2020</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>1</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
9675
dep/CLI11/CLI11.hpp
@@ -1,5 +0,0 @@
|
||||
# CLI11
|
||||
|
||||
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)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{"Registrations":[
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/CLIUtils/CLI11",
|
||||
"commitHash": "dd0d8e4fe729e5b1110232c7a5c9566dad884686"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
2
dep/gsl
@@ -2,15 +2,6 @@
|
||||
|
||||
[Amalgamated](https://github.com/open-source-parsers/jsoncpp/wiki/Amalgamated)
|
||||
from source commit
|
||||
[6aba23f](https://github.com/open-source-parsers/jsoncpp/commit/6aba23f4a8628d599a9ef7fa4811c4ff6e4070e2),
|
||||
release 1.9.3.
|
||||
[ddabf50](https://github.com/open-source-parsers/jsoncpp/commit/ddabf50f72cf369bf652a95c4d9fe31a1865a781),
|
||||
release 1.8.4.
|
||||
|
||||
> 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
|
||||
> ```
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{"Registrations":[
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/open-source-parsers/jsoncpp",
|
||||
"commitHash": "6aba23f4a8628d599a9ef7fa4811c4ff6e4070e2"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
@@ -79,151 +79,6 @@ 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
|
||||
// //////////////////////////////////////////////////////////////////////
|
||||
@@ -235,14 +90,19 @@ bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
|
||||
|
||||
#ifndef JSON_CONFIG_H_INCLUDED
|
||||
#define JSON_CONFIG_H_INCLUDED
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#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
|
||||
|
||||
// If non-zero, the library uses exceptions to report bad input instead of C
|
||||
// assertion macros. The default is to use exceptions.
|
||||
@@ -250,132 +110,164 @@ bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
|
||||
#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
|
||||
|
||||
// Export macros for DLL visibility
|
||||
#if defined(JSON_DLL_BUILD)
|
||||
#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)
|
||||
#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_DLL_BUILD
|
||||
|
||||
#endif // ifdef JSON_IN_CPPTL
|
||||
#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
|
||||
|
||||
// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
|
||||
// C++11 should be used directly in JSONCPP.
|
||||
#define JSONCPP_OVERRIDE override
|
||||
#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
|
||||
|
||||
#ifdef __clang__
|
||||
#if __has_extension(attribute_deprecated_with_message)
|
||||
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
|
||||
#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__
|
||||
|
||||
#if !defined(JSONCPP_DEPRECATED)
|
||||
#define JSONCPP_DEPRECATED(message)
|
||||
#endif // if !defined(JSONCPP_DEPRECATED)
|
||||
|
||||
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
|
||||
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
|
||||
#if __GNUC__ >= 6
|
||||
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
|
||||
#endif
|
||||
|
||||
#if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
#include "allocator.h"
|
||||
#include "version.h"
|
||||
# include "version.h"
|
||||
|
||||
# if JSONCPP_USING_SECURE_MEMORY
|
||||
# include "allocator.h" //typedef Allocator
|
||||
# endif
|
||||
|
||||
#endif // if !defined(JSON_IS_AMALGAMATION)
|
||||
|
||||
namespace Json {
|
||||
using Int = int;
|
||||
using UInt = unsigned int;
|
||||
typedef int Int;
|
||||
typedef unsigned int UInt;
|
||||
#if defined(JSON_NO_INT64)
|
||||
using LargestInt = int;
|
||||
using LargestUInt = unsigned int;
|
||||
typedef int LargestInt;
|
||||
typedef unsigned int LargestUInt;
|
||||
#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
|
||||
using Int64 = __int64;
|
||||
using UInt64 = unsigned __int64;
|
||||
typedef __int64 Int64;
|
||||
typedef unsigned __int64 UInt64;
|
||||
#else // if defined(_MSC_VER) // Other platforms, use long long
|
||||
using Int64 = int64_t;
|
||||
using UInt64 = uint64_t;
|
||||
#endif // if defined(_MSC_VER)
|
||||
using LargestInt = Int64;
|
||||
using LargestUInt = UInt64;
|
||||
typedef int64_t Int64;
|
||||
typedef uint64_t UInt64;
|
||||
#endif // if defined(_MSC_VER)
|
||||
typedef Int64 LargestInt;
|
||||
typedef UInt64 LargestUInt;
|
||||
#define JSON_HAS_INT64
|
||||
#endif // if defined(JSON_NO_INT64)
|
||||
|
||||
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;
|
||||
#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
|
||||
|
||||
#endif // JSON_CONFIG_H_INCLUDED
|
||||
|
||||
@@ -407,23 +299,17 @@ using JSONCPP_OSTREAM = Json::OStream;
|
||||
namespace Json {
|
||||
|
||||
// writer.h
|
||||
class StreamWriter;
|
||||
class StreamWriterBuilder;
|
||||
class Writer;
|
||||
class FastWriter;
|
||||
class StyledWriter;
|
||||
class StyledStreamWriter;
|
||||
|
||||
// reader.h
|
||||
class Reader;
|
||||
class CharReader;
|
||||
class CharReaderBuilder;
|
||||
|
||||
// json_features.h
|
||||
// features.h
|
||||
class Features;
|
||||
|
||||
// value.h
|
||||
using ArrayIndex = unsigned int;
|
||||
typedef unsigned int ArrayIndex;
|
||||
class StaticString;
|
||||
class Path;
|
||||
class PathArgument;
|
||||
|
||||
2
dep/wil
@@ -8,9 +8,9 @@ Settings in the Windows Console Host can be a bit tricky to understand. This is
|
||||
|---------------------------|-----------------------|--------------------------------------|
|
||||
|`FontSize` |Coordinate (REG_DWORD) |Size of font in pixels |
|
||||
|`FontFamily` |REG_DWORD |GDI Font family |
|
||||
|`ScreenBufferSize` |Coordinate (REG_DWORD) |Size of the screen buffer in WxH characters\*\* |
|
||||
|`ScreenBufferSize` |Coordinate (REG_DWORD) |Size of the screen buffer in WxH characters |
|
||||
|`CursorSize` |REG_DWORD |Cursor height as percentage of a single character |
|
||||
|`WindowSize` |Coordinate (REG_DWORD) |Initial size of the window in WxH characters\*\* |
|
||||
|`WindowSize` |Coordinate (REG_DWORD) |Initial size of the window in WxH characters |
|
||||
|`WindowPosition` |Coordinate (REG_DWORD) |Initial position of the window in WxH pixels (if not set, use auto-positioning) |
|
||||
|`WindowAlpha` |REG_DWORD |Opacity of the window (valid range: 0x4D-0xFF) |
|
||||
|`ScreenColors` |REG_DWORD |Default foreground and background colors |
|
||||
@@ -39,10 +39,6 @@ Settings in the Windows Console Host can be a bit tricky to understand. This is
|
||||
|
||||
*: Only applies to the improved version of the Windows Console Host
|
||||
|
||||
**: WxH stands for Width by Height, it's the fact that things like a Window size
|
||||
store the Width and Height values in the high and low word in the registry's
|
||||
double word values.
|
||||
|
||||
## The Settings Hierarchy
|
||||
|
||||
Settings are persisted to a variety of locations depending on how they are modified and how the Windows Console Host was invoked:
|
||||
|
||||
@@ -7,31 +7,4 @@ This file contains notes about debugging various items in the repository.
|
||||
If you want to debug code in the Cascadia package via Visual Studio, your breakpoints will not be hit by default. A tweak is required to the *CascadiaPackage* project in order to enable this.
|
||||
|
||||
1. Right-click on *CascadiaPackage* in Solution Explorer and select Properties.
|
||||
2. Change the *Application process* type from *Mixed (Managed and Native)* to *Native Only*.
|
||||
|
||||
## Popping into the Debugger from Running Code
|
||||
|
||||
Sometimes you will encounter a scenario where you need to break into the console or terminal code under the debugger but you cannot, for whatever reason, do so by launching it from the beginning under the debugger. This can be especially useful for debugging tests with TAEF which usually launch through several child processes and modules before hitting your code.
|
||||
|
||||
To accomplish this, add a `DebugBreak()` statement somewhere in the code and ensure you have a Post-Mortem debugger set.
|
||||
|
||||
**NOTE:** `conhost.exe` already has a provision for a conditional `DebugBreak()` very early in the startup code if it was built in debug mode. Set `HKCU\Console` with `DebugLaunch` as a `REG_DWORD` with the value of `1`.
|
||||
|
||||
### Setting Visual Studio as Post Mortem Debugger
|
||||
|
||||
Go to `Tools > Options` and then make sure that `Native` is checked as the `Just-In-Time Debugging` provider. (Checking the box, if it is not checked, will require that Visual Studio is launched as Administrator.)
|
||||
|
||||

|
||||
|
||||
Then when you run something with `DebugBreak()` in it, you will see this:
|
||||

|
||||
|
||||
The top ones will be new instances of the Visual Studios installed on your system. The bottom ones will be the running instances of Visual Studio. You can see in the image that one is open already. If you choose the bottom one, VS will attach straight up as if you F5'd from the solution at the point from the `DebugBreak()`. Step up to get out of the break and back into the code.
|
||||
|
||||
### Setting WinDBG as Post Mortem Debugger
|
||||
|
||||
From an elevated context (a command prompt or whatnot...), run `windbg /I`. This will install the debugger as Post Mortem.
|
||||
|
||||
Then run the thing and it will pop straight into a new WinDBG session. Step up to get out of the break and back into the code.
|
||||
|
||||
**Caveat:** If you are on an x64 system, you may need to do `windbg /I` with both the x64 and x86 versions of the debugger to catch all circumstances (like if you're trying to run x86 code.)
|
||||
2. Change the *Application process* type from *Mixed (Managed and Native)* to *Native Only*.
|
||||
181
doc/Niksa.md
@@ -1,181 +0,0 @@
|
||||
# Niksa's explanations
|
||||
|
||||
Sometimes @miniksa will write a big, long explanatory comment in an issue thread that turns out to be a decent bit of reference material.
|
||||
This document serves as a storage point for those posts.
|
||||
|
||||
- [Why do we avoid changing CMD.exe?](#cmd)
|
||||
- [Why is typing-to-screen performance better than every other app?](#screenPerf)
|
||||
- [How are the Windows graphics/messaging stack assembled?](#gfxMsgStack)
|
||||
- [Output Processing between "Far East" and "Western"](#fesb)
|
||||
- [Why do we not backport things?](#backport)
|
||||
- [Why can't we have mixed elevated and non-elevated tabs in the Terminal?](#elevation)
|
||||
|
||||
## <a name="cmd"></a>Why do we avoid changing CMD.exe?
|
||||
`setlocal` doesn't behave the same way as an environment variable. It's a thing that would have to be put in at the top of the batch script that is `somefile.cmd` as one of its first commands to adjust the way that one specific batch file is processed by the `cmd.exe` engine. That's probably not suitable for your needs, but that's the way we have to go.
|
||||
|
||||
I don't think anyone is disagreeing with you, @mikemaccana, that this would be a five minute development change to read that environment variable and change the behavior of `cmd.exe`. It absolutely would be a tiny development time.
|
||||
|
||||
It's just that from our experience, we know there's going to be a 3-24 month bug tail here where we get massive investigation callbacks by some billion dollar enterprise customer who for whatever reason was already using the environment variable we pick for another purpose. Their script that they give their rank-and-file folks will tell them to press Ctrl+C at some point in the batch script to do whatever happens, it will do something different, those people will notice the script doesn't match the computer anymore. They will then halt the production line and tell their supervisor. The supervisor tells some director. Their director comes screaming at their Microsoft enterprise support contract person that we've introduced a change to the OS that is costing them millions if not billions of dollars in shipments per month. Our directors at Microsoft then come bashing down our doors angry with us and make us fix it ASAP or revert it, we don't get to go home at 5pm to our families or friends because we're fixing it, we get stressed the heck out, we have to spin up servicing potentially for already shipped operating systems which is expensive and headache-causing...etc.
|
||||
|
||||
We can see this story coming a million miles away because it has happened before with other 'tiny' change we've been asked to make to `cmd.exe` in the past few years.
|
||||
|
||||
I would just ask you to understand that `cmd.exe` is very, very much in a maintenance mode and I just want to set expectations here. We maintain it, yes. We have a renewed interest in command-line development, yes. But our focuses are revolving around improving the terminal and platform itself and bringing modern, supported shells to be the best they can be on Windows. Paul will put this on the backlog of things that people want in `cmd.exe`, yes. But it will sink to the bottom of the backlog because changing `cmd.exe` is our worst nightmare as its compatibility story is among the heaviest of any piece of the operating system.
|
||||
|
||||
I would highly recommend that Gulp convert to using PowerShell scripts and that if such an issue exists with PowerShell, that we get their modern, supported, and better-engineered platform to support the scenario. I don't want you to sit around waiting for `cmd.exe` to change this because it's really not going to happen faster than that script could be converted to `ps1` and it fixed in PowerShell Core (if that's even a problem in that world.)
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/217#issuecomment-404240443
|
||||
|
||||
## <a name="screenPerf"></a>Why is typing-to-screen performance better than every other app?
|
||||
|
||||
I really do not mind when someone comes by and decides to tell us that we're doing a good job at something. We hear so many complaints every day that a post like this is a breath of fresh air. Thanks for your thanks!
|
||||
|
||||
Also, I'm happy to discuss this with you until you're utterly sick of reading it. Please ask any follow-ons you want. I thrive on blathering about my work. :P
|
||||
|
||||
If I had to take an educated guess as to what is making us faster than pretty much any other application on Windows at putting your text on the screen... I would say it is because that is literally our only job! Also probably because we are using darn near the oldest and lowest level APIs that Windows has to accomplish this work.
|
||||
|
||||
Pretty much everything else you've listed has some sort of layer or framework involved, or many, many layers and frameworks, when you start talking about Electron and JavaScript. We don't.
|
||||
|
||||
We have one bare, super un-special window with no additional controls attached to it. We get our keys fed into us from just barely above the kernel given that we're processing them from window messages and not from some sort of eventing framework common to pretty much any other more complicated UI framework than ours (WPF, WinForms, UWP, Electron). And we dump our text straight onto the window surface using GDI's [PolyTextOut](https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-polytextoutw) with no frills.
|
||||
|
||||
Even `notepad.exe` has multiple controls on its window at the very least and is probably (I haven't looked) using some sort of library framework in the edit control to figure out its text layout (which probably is using another library framework for internationalization support...)
|
||||
|
||||
Of course this also means that we have trade offs. We don't support fully international text like pretty much every other application will. RTL? No go zone right now. Surrogate pairs and emoji? We're getting there but not there yet. Indic scripts? Nope.
|
||||
|
||||
Why are we like this? For one, `conhost.exe` is old as dirt. It has to use the bare metal bottom layer of everything because it was created before most of those other frameworks were created. And also it maintains as low/bottom level as possible because it is pretty much the first thing that one needs to bring up when bringing up a new operating system edition or device before you have all the nice things like frameworks or what those frameworks require to operate. Also it's written in C/C++ which is about as low and bare metal as we can get.
|
||||
|
||||
Will this UI enhancement come to other apps on Windows? Almost certainly not. They have too much going on which is both a good and a bad thing. I'm jealous of their ability to just call one method and layout text in an uncomplicated manner in any language without manually calculating pixels or caring about what styles apply to their font. But my manual pixel calculations, dirty region math, scroll region madness, and more makes it so we go faster than them. I'm also jealous that when someone says "hey can you add a status bar to the bottom of your window" that they can pretty much click and drag that into place with their UI Framework and it will just work where as for us, it's been a backlog item forever and gives me heartburn to think about implementing.
|
||||
|
||||
Will we try to keep it from regressing? Yes! Right now it's sort of a manual process. We identify that something is getting slow and then we go haul out [WPR](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-recorder) and start taking traces. We stare down the hot paths and try to reason out what is going on and then improve them. For instance, in the last cycle or two, we focused on heap allocations as a major area where we could improve our end-to-end performance, changing a ton of our code to use stack-constructed iterator-like facades over the underlying request buffer instead of translating and allocating it into a new heap space for each level of processing.
|
||||
|
||||
As an aside, @bitcrazed wants us to automate performance tests in some conhost specific way, but I haven't quite figured out a controlled environment to do this in yet. The Windows Engineering System runs performance tests each night that give us a coarse grained way of knowing if we messed something up for the whole operating system, and they technically offer a fine grained way for us to insert our own performance tests... but I just haven't got around to that yet. If you have an idea for a way for us to do this in an automated fashion, I'm all ears.
|
||||
|
||||
If there's anything else you'd like to know, let me know. I could go on all day. I deleted like 15 tangents from this reply before posting it....
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/327#issuecomment-447391705
|
||||
|
||||
## <a name="gfxMsgStack"></a>How are the Windows graphics/messaging stack assembled?
|
||||
|
||||
@stakx, I am referring to USER32 and GDI32.
|
||||
|
||||
I'll give you a cursory overview of what I know off the top of my head without spending hours confirming the details. As such, some of this is subject to handwaving and could be mildly incorrect but is probably in the right direction. Consider every statement to be my personal knowledge on how the world works and subject to opinion or error.
|
||||
|
||||
For the graphics part of the pipeline (GDI32), the user-mode portions of GDI are pretty far down. The app calls GDI32, some work is done in that DLL on the user-mode side, then a kernel call jumps over to the kernel and drawing occurs.
|
||||
|
||||
The portion that you're thinking of regarding "silently converted to sit on top of other stuff" is probably that once we hit the kernel calls, a bunch of the kernel GDI stuff tends to be re-platformed on top of the same stuff as DirectX when it is actually handled by the NVIDIA/AMD/Intel/etc. graphics driver and the GPU at the bottom of the stack. I think this happened with the graphics driver re-architecture that came as a part of WDDM for Windows Vista. There's a document out there somewhere about what calls are still really fast in GDI and which are slower as a result of the re-platforming. Last time I found that document and checked, we were using the fast ones.
|
||||
|
||||
On top of GDI, I believe there are things like Common Controls or comctl32.dll which provided folks reusable sets of buttons and elements to make their UIs before we had nicer declarative frameworks. We don't use those in the console really (except in the property sheet off the right click menu).
|
||||
|
||||
As for DirectWrite and D2D and D3D and DXGI themselves, they're a separate set of commands and paths that are completely off to the side from GDI at all both in user and kernel mode. They're not really related other than that there's some interoperability provisions between the two. Most of our other UI frameworks tend to be built on top of the DirectX stack though. XAML is for sure. I think WPF is. Not sure about WinForms. And I believe the composition stack and the window manager are using DirectX as well.
|
||||
|
||||
As for the input/interaction part of the pipeline (USER32), I tend to find most other newer things (at least for desktop PCs) are built on top of what is already there. USER32's major concept is windows and window handles and everything is sent to a window handle. As long as you're on a desktop machine (or a laptop or whatever... I mean a classic-style Windows-powered machine), there's a window handle involved and messages floating around and that means we're talking USER32.
|
||||
|
||||
The window message queue is just a straight up FIFO (more or less) of whatever input has occurred relevant to that window while it's in the foreground + whatever has been sent to the window by other components in the system.
|
||||
|
||||
The newer technologies and the frameworks like XAML and WPF and WinForms tend to receive the messages from the window message queue one way or another and process them and turn them into event callbacks to various objects that they've provisioned within their world.
|
||||
|
||||
However, the newer technologies that also work on other non-desktop platforms like XAML tend to have the ability to process stuff off of a completely different non-USER32 stack as well. There's a separate parallel stack to USER32 with all of our new innovations and realizations on how input and interaction should occur that doesn't exactly deal with classic messaging queues and window handles the same way. This is the whole Core* family of things like CoreWindow and CoreMessaging. They also have a different concept of "what is a user" that isn't so centric around your butt in rolling chair in front of a screen with a keyboard and mouse on the desk.
|
||||
|
||||
Now, if you're on XAML or one of the other Frameworks... all this intricacy is handled for you. XAML figures out how to draw on DirectX for you and negotiates with the compositor and window manager for cool effects on your behalf. It figures out whether to get your input events from USER32 or Core* or whatever transparently depending on your platform and the input stacks can handle pen, touch, keyboard, mouse, and so on in a unified manner. It has provisions inside it embedded to do all the sorts of globalization, accessibility, input interaction, etc. stuff that make your life easy. But you could choose to go directly to the low-level and handle it yourself or skip handling what you don't care about.
|
||||
|
||||
The trick is that GDI32 and USER32 were designed for a limited world with a limited set of commands. Desktop PCs were the only thing that existed, single user at the keyboard and mouse, simple graphics output to a VGA monitor. So using them directly at the "low level" like conhost does is pretty easy. The new platforms could be used at the "low level" but they're orders of magnitude more complicated because they now account for everything that has happened with personal computing in 20+ years like different form factors, multiple active users, multiple graphics adapters, and on and on and on and on. So you tend to use a framework when using the new stuff so your head doesn't explode. They handle it for you, but they handle more than they ever did before so they're slower to some degree.
|
||||
|
||||
So are GDI32 and USER32 "lower" than the new stuff? Sort of.
|
||||
Can you get that low with the newer stuff? Mostly yes, but you probably shouldn't and don't want to.
|
||||
Does new live on top of old or is old replatformed on the new? Sometimes and/or partially.
|
||||
Basically... it's like the answer to anything software... "it's an unmitigated disaster and if we all stepped back a moment, we should be astounded that it works at all." :P
|
||||
|
||||
Anyway, that's enough ramble for one morning. Hopefully that somewhat answered your questions and gave you a bit more insight.
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/327#issuecomment-447926388
|
||||
|
||||
## <a name="fesb"></a>Output Processing between "Far East" and "Western"
|
||||
|
||||
>
|
||||
> ```
|
||||
> if (WI_IsFlagSet(CharType, C1_CNTRL))
|
||||
> ```
|
||||
|
||||
In short, this is probably fine to fix.
|
||||
|
||||
However, I would personally feed a few characters through `WriteCharsLegacy` under the debugger and assert that your theory is correct first (that multiple flags coming back are what the problem is) before making the change.
|
||||
|
||||
I am mildly terrified, less than Dustin, because it is freaking `WriteCharsLegacy` which is the spawn of hell and I fear some sort of regression in it.
|
||||
|
||||
In long, why is it fine to fix?
|
||||
|
||||
For reference, this particular segment of code https://github.com/microsoft/terminal/blob/9b92986b49bed8cc41fde4d6ef080921c41e6d9e/src/host/_stream.cpp#L514-L539 appears to only be used when the codepoint is < 0x20 or == 0x7F https://github.com/microsoft/terminal/blob/9b92986b49bed8cc41fde4d6ef080921c41e6d9e/src/host/_stream.cpp#L408 and ENABLE_PROCESSED_OUTPUT is off. https://github.com/microsoft/terminal/blob/9b92986b49bed8cc41fde4d6ef080921c41e6d9e/src/host/_stream.cpp#L320
|
||||
|
||||
I looked back at the console v1 code and this particular section had a divergence for "Western" countries and "Far East" countries (a geopolitically-charged term, but what it was, nonetheless.)
|
||||
|
||||
For "Western" countries, we would unconditionally run all the characters through `MultiByteToWideChar` with `MB_USEGLYPHCHARS` without the `C1_CNTRL` test and move the result into the buffer.
|
||||
|
||||
For "Eastern" countries, we did the `C1_CNTRL` test and then if true, we would run through `MultiByteToWideChar` with `MB_USEGLYPHCHARS`. Otherwise, we would just move the original character into the buffer and call it a day.
|
||||
|
||||
Note in both of these, there is a little bit of indirection before `MultiByteToWideChar` is called through some other helper methods like `ConvertOutputToUnicode`, but that's the effective conversion point, as far as I can tell. And that's where the control characters would turn into acceptable low ASCII symbols.
|
||||
|
||||
When we took over the console codebase, this variation between "Western" and "Eastern" countries was especially painful because `conhost.exe` would choose which one it was in based on the `Codepage for Non-Unicode Applications` set in the Control Panel's Regional > Administrative panel and it could only be changed with a reboot. It wouldn't even change properly when you `chcp` to a different codepage. Heck, `chcp` would deny you from switching into many codepages. There was a block in place to prevent going to an "Eastern" codepage if you booted up in a "Western" codepage. There was also a block preventing you from going between "Eastern" codepages, if I recall correctly.
|
||||
|
||||
In modernizing, I decided a few things:
|
||||
1. What's good for the "Far East" should be good for the rest of the world. CJK languages that encompassed the "Far East" code have to be able to handle "Western" text as well even if the reverse wasn't true.
|
||||
2. We need to scrub all usages of "Far East" from the code. Someone already started that and replaced them with "East Asia" except then they left behind the shorthand of "FE" prefixing dozens of functions which made it hard to follow the code. It took us months to realize "FE" and "East Asia" were the same thing.
|
||||
3. It's obnoxious that the way this was handled was to literally double-define every output function in the code base to have two definitions, compile them both into the conhost, then choose to run down the SB_ versions or the FE_ versions depending on the startup Non-Unicode codepage. It was a massive pile of complex pre-compilation `#ifdef` and `#else`s that would sometimes surround individual lines in the function bodies. Gross.
|
||||
4. The fact that the FE_ versions of the functions were way slower than the SB_ ones was unacceptable even for the same output of Latin-character text.
|
||||
5. Anyone should be free to switch between any codepage they want at any time and restricting it based on a value from OS startup or region/locale is not acceptable in the modern world.
|
||||
6. I concluded by all of the above that I was going to tank/delete/remove the SB_ versions of everything and force the entire world to use the FE_ versions as truth. I would fix the FE_ versions to handle everything correctly, I would fix the performance characteristics of the FE_ versions so they were only slower when things were legitimately more complicated and never otherwise, I would banish all usage of "Far East", "East Asia", "FE_", and "SB_" from the codebase, and codepages would be freely switchable.
|
||||
7. Oh. Also, the conhost used to rewrite its entire backing buffer into whatever your current codepage was whenever you switched codepages. I changed that to always hold it as UTF-16.
|
||||
|
||||
Now, after that backstory. This is where the problem comes in. It looks like the code you're pointing to that didn't check flags and instead checked direct equality... is the way that it was ALWAYS done for the "Eastern" copy of the code. So it was ALWAYS broken for the "Eastern" codepages and country variants of the OS.
|
||||
|
||||
I don't know why the "Eastern" copy was checking `C1_CNTRL` at all in the first place. There is no documentation. I presume it has to do with Shift-JIS or GB-2312 or Unified Hangul or something having a conflict < 0x20 || == 0x7F. Or alternatively, it's because someone wrote the code naively thinking it was a good idea in a hurry and never tested it. Very possible and even probable.
|
||||
|
||||
Presuming CJK codepages have no conflict in this range for their DBCS codepages... we could probably remove the check with `GetStringTypeW` entirely and always run everything through `ConvertOutputToUnicode`. More risky than just the flag test change... but theoretically an option as well.
|
||||
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/166#issuecomment-510953359
|
||||
|
||||
## <a name="backport"></a>Why do we not backport things?
|
||||
|
||||
Someone has to prove that this is costing millions to billions of dollars of lost productivity or revenue to outweigh the risks of shipping the fix to hundreds of millions of Windows machines and potentially breaking something.
|
||||
|
||||
Our team generally finds it pretty hard to prove that against the developer audience given that they're only a small portion of the total installed market of Windows machines.
|
||||
|
||||
Our only backport successes really come from corporations with massive addressable market (like OEMs shipping PCs) who complain that this is fouling up their manufacturing line (or something of that ilk). Otherwise, our management typically says that the risks don't outweigh the benefits.
|
||||
|
||||
It's also costly in terms of time, effort, and testing for us to validate a modification to a released OS. We have a mindbogglingly massive amount of automated machinery dedicated to processing and validating the things that we check in while developing the current OS builds. But it's a special costly ask to spin up some to all of those activities to validate backported fixes. We do it all the time for Patch Tuesday, but in those patches, they only pass through the minimum number of fixes required to maximize the restoration of productivity/security/revenue/etc. because every additional fix adds additional complexity and additional risk.
|
||||
|
||||
So from our little team working hard to make developers happy, we virtually never make the cut for servicing. We're sorry, but we hope you can understand. It's just the reality of the situation to say "nope" when people ask for a backport. In our team's ideal world, you would all be running the latest console bits everywhere everytime we make a change. But that's just not how it is today.
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/279#issuecomment-439179675
|
||||
|
||||
## <a name="elevation"></a>Why can't we have mixed elevated and non-elevated tabs in the Terminal?
|
||||
|
||||
_guest speaker @DHowett-MSFT_
|
||||
|
||||
[1] It is trivial when you are _hosting traditional windows_ with traditional window handles. That works very well in the conemu case, or in the tabbed shell case, where you can take over a window in an elevated session and re-parent it under a window in a non-elevated session.
|
||||
|
||||
When you do that, there's a few security features that I'll touch on in [2]. Because of those, you can parent it but you can't really force it to do anything.
|
||||
|
||||
There's a problem, though. The Terminal isn't architected as a collection of re-parentable windows. For example, it's not running a console host and moving its window into a tab. It was designed to support a "connection" -- something that can read and write text. It's a lower-level primitive than a window. We realized the error of our ways and decided that the UNIX model was right the entire time, and pipes and text and streams are _where it's at._
|
||||
|
||||
Given that we're using Xaml islands to host a modern UI and stitching a DirectX surface into it, we're far beyond the world of standard window handles anyway. Xaml islands are fully composed into a single HWND, much like Chrome and Firefox and the gamut of DirectX/OpenGL/SDL games. We don't **have** components that can be run in one process (elevated) and hosted in another (non-elevated) that aren't the aforementioned "connections".
|
||||
|
||||
Now, the obvious followup question is _"why can't you have one elevated connection in a tab next to a non-elevated connection?"_ This is where @sba923 should pick up reading (:smile:). I'm probably going to cover some things that you (@robomac) know already.
|
||||
|
||||
[2] When you have two windows on the same desktop in the same window station, they can communicate with eachother. I can use `SendKeys` easily through `WScript.Shell` to send keyboard input to any window that the shell can see.
|
||||
|
||||
Running a process elevated _severs_ that connection. The shell can't see the elevated window. No other program at the same integrity level as the shell can see the elevated window. Even if it has its window handle, it can't really interact with it. This is also why you can't drag/drop from explorer into notepad if notepad is running elevated. Only another elevated process can interact with another elevated window.
|
||||
|
||||
That "security" feature (call it what you like, it was probably intended to be a security feature at one point) only exists for a few session-global object types. Windows are one of them. Pipes aren't really one of them.
|
||||
|
||||
Because of that, it's trivial to break that security. Take the terminal as an example of that. If we start an elevated connection and host it in a _non-elevated_ window, we've suddenly created a conduit through that security boundary. The elevated thing on the other end isn't a window, it's just a text-mode application. It immediately does the bidding of the non-elevated host.
|
||||
|
||||
Anybody that can _control_ the non-elevated host (like `WScript.Shell::SendKeys`) _also_ gets an instant conduit through the elevation boundary. Suddenly, any medium integrity application on your system can control a high-integrity process. This could be your browser, or the bitcoin miner that got installed with the `left-pad` package from NPM, or really any number of things.
|
||||
|
||||
It's a small risk, but it _is_ a risk.
|
||||
|
||||
---
|
||||
|
||||
Other platforms have accepted that risk in preference for user convenience. They aren't wrong to do so, but I think Microsoft gets less of a "pass" on things like "accepting risk for user convenience". Windows 9x was an unmitigated security disaster, and limited user accounts and elevation prompts and kernel-level security for window management were the answer to those things. They're not locks to be loosened lightly.
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/632#issuecomment-519375707
|
||||
|
||||
@@ -24,16 +24,6 @@
|
||||
* `/ipch` – not checked in is where intellisense data will be generated if you use Visual Studio 2015
|
||||
* `/obj` – not checked in is where objects will be generated by the MSBuild system
|
||||
* `/src` – This is the fun one. In the root is common build system data.
|
||||
* `/src/cascadia` - This directory contains all the code specific to the Windows Terminal
|
||||
* `/src/cascadia/TerminalConnection` - This DLL is responsible for the various different ways a terminal instance can communicate with different terminal backends. Examples include the `ConptyConnection` (for communicating with Windows Console processes), or the `AzureCloudShellConnection` for communicating with Azure.
|
||||
* `/src/cascadia/TerminalSettings` - This is the DLL responsible for abstracting the settings for both the TerminalCore and the TerminalControl. This provides consumers of the TerminalControl a common interface for supplying settings to the Terminal.
|
||||
* `/src/cascadia/TerminalCore` - This LIB is responsible for the core implementation of a terminal instance. This defines one important class `Terminal` which is a complete terminal instance, with buffer, colors table, VT parsing, input handling, etc. It does _not_ prescribe any sort of UI implementation - it should be connected to code that can handle rendering its contents, and provide input to it.
|
||||
* `/src/cascadia/TerminalControl` - This DLL provides the UWP-XAML implementation of a `TermControl`, which can be embedded within an application to provide a terminal instance within the application. It contains a DX renderer for drawing text to the screen, and translates input to send to the core Terminal. It also receives settings to apply to both itself and the core Terminal.
|
||||
* `/src/cascadia/TerminalApp` - This DLL represents the implementation of the Windows Terminal application. This includes parsing settings, hosting tabs & panes with Terminals in them, and displaying other UI elements. This DLL is almost entirely UWP-like code, and shouldn't be doing any Win32-like UI work.
|
||||
* `/src/cascadia/WindowsTerminal` - This EXE provides Win32 hosting for the TerminalApp. It will set up XAML islands, and is responsible for drawing the window, either as a standard window or with content in the titlebar (non-client area).
|
||||
* `/src/cascadia/CascadiaPackage` - This is a project for packaging the Windows Terminal and its dependencies into an .appx/.msix for deploying to the machine.
|
||||
* `/src/cascadia/PublicTerminalCore` - This is a DLL wrapper for the TerminalCore and Renderer, similar to `TermControl`, which exposes some exported functions that so the Terminal can be used from C#.
|
||||
* `/src/cascadia/WpfTerminalControl` - A DLL implementing a WPF version of the Terminal Control.
|
||||
* `/src/host` – The meat of the windows console host. This includes buffer, input, output, windowing, server management, clipboard, and most interactions with the console host window that aren’t stated anywhere else. We’re trying to pull things out that are reusable into other libraries, but it’s a work in progress
|
||||
* `/src/host/lib` – Builds the reusable LIB copy of the host
|
||||
* `/src/host/dll` – Packages LIB into conhostv2.dll to be put into the OS C:\windows\system32\
|
||||
@@ -52,7 +42,7 @@
|
||||
* `/src/renderer/base` – Base interface layer providing non-engine-specific rendering things like choosing the data from the console buffer, deciding how to lay out or transform that data, then dispatching commands to a specific final display engine
|
||||
* `/src/renderer/gdi` – The GDI implementation of rendering to the screen. Takes commands to “draw a line” or “fill the background” or “select a region” from the base and turns them into GDI calls to the screen. Extracted from original console host code.
|
||||
* `/src/renderer/inc` – Interface definitions for all renderer communication
|
||||
* `/src/terminal` – Virtual terminal support for the console. This is the sequences that are found in-band with other text on STDIN/STDOUT that command the display to do things. This is the \*nix way of controlling a console.
|
||||
* `/src/terminal` – Virtual terminal support for the console. This is the sequences that are found in-band with other text on STDIN/STDOUT that command the display to do things. This is the *nix way of controlling a console.
|
||||
* `/src/terminal/parser` – This contains a state machine and sorting engine for feeding in individual characters from STDOUT or STDIN and decoding them into the appropriate verbs that should be performed
|
||||
* `/src/terminal/adapter` – This converts the verbs from the interface into calls on the console API. It doesn’t actually call through the API (for performance reasons since it lives inside the same binary), but it tries to remain as close to an API call as possible. There are some private extensions to the API for behaviors that didn’t exist before this was written that we’ve not made public. We don’t know if we will yet or force people to use VT to get at them.
|
||||
* `/src/tsf` – Text Services Foundation. This provides IME input services to the console. This was historically used for only Chinese, Japanese, and Korean IMEs specifically on OS installations with those as the primary language. It was in the summer of 2016 unrestricted to be able to be used on any OS installation with any IME (whether or not it will display correctly is a different story). It also was unrestricted to allow things like Pen and Touch input (which are routed via IME messages) to display properly inside the console from the TabTip window (the little popup that helps you insert pen/touch writing/keyboard candidates into an application)
|
||||
@@ -100,7 +90,7 @@
|
||||
* Assorted utilities and stuff
|
||||
* `Misc.cpp` (left for us by previous eras of random console devs)
|
||||
* `Util.cpp` (created in our era)
|
||||
* Custom zeroing and non-throwing allocator
|
||||
* Custom zeroing and non-throwing allocator
|
||||
* `Newdelete.cpp`
|
||||
* Related to inserting text into the TextInfo buffer
|
||||
* `Output.cpp`
|
||||
|
||||
@@ -5,4 +5,3 @@
|
||||
1. If it's brand new code or refactoring a complete class or area of the code, please follow as Modern C++ of a style as you can and reference the [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) as much as you possibly can.
|
||||
1. When working with any Win32 or NT API, please try to use the [Windows Implementation Library](./WIL.md) smart pointers and result handlers.
|
||||
1. The use of NTSTATUS as a result code is discouraged, HRESULT or exceptions are preferred. Functions should not return a status code if they would always return a successful status code. Any function that returns a status code should be marked `noexcept` and have the `nodiscard` attribute.
|
||||
1. When contributing code in `TerminalApp`, be mindful to appropriately use C++/WinRT [strong and weak references](https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/weak-references), and have a good understanding of C++/WinRT [concurrency schemes](https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/concurrency).
|
||||
|
||||
42
doc/bot.md
@@ -8,12 +8,12 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
### Quick-Guidance to Core Contributors
|
||||
1. Look at `Needs-Attention` as top priority
|
||||
1. Look at `Needs-Triage` during triage meetings to get a handle on what's new and sort it out
|
||||
1. Look at `Needs-Tag-Fix` when you have a few minutes to fix up things tagged improperly
|
||||
1. Look at `Needs-Tag-Fix` when you have a few minutes to fix up things tagged impoperly
|
||||
1. Manually add `Needs-Author-Feedback` when there's something we need the author to follow up on and want attention if they return it or an auto-close for inactivity if it goes stale.
|
||||
|
||||
### Tagging/Process Details
|
||||
1. When new issues arrive, or when issues are not properly tagged... we'll mark them as `Needs-Triage` automatically.
|
||||
- The core contributor team will then come through and mark them up as appropriate. The goal is to have a tag that fits the `Product`, `Area`, and `Issue` category.
|
||||
- The core contributor team will then come through and mark them up as appropriate. The goal is to have a tag that fits the `Product`, `Area`, and `Issue` category.
|
||||
- The `Needs-Triage` tag will be removed manually by the core contributor team during a triage meeting. (Exception, triage may also be done offline by senior team members during high-volume times.)
|
||||
- An issue may or may not be assigned to a contributor during triage. It is not necessary to assign someone to complete it.
|
||||
- We're not focusing on Projects yet.
|
||||
@@ -22,7 +22,7 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
- When this tag drops off, the bot will apply the `Needs-Attention` tag to get the core contribution team's attention again. If an author cares enough to be active, we will attempt to prioritize engaging with that author.
|
||||
- If the author doesn't come back around in a while, this will become a `No-Recent-Activity` tag.
|
||||
- If there's activity on an issue, the `No-Recent-Activity` tag will automatically drop.
|
||||
- If the `No-Recent-Activity` stays, the issue will be closed as stale.
|
||||
- If the `No-Recent-Activity` stays, the issue will be closed as stale.
|
||||
1. PRs will automatically get a `Needs-Author-Feedback` tag when reviewers wait on the author
|
||||
- This follows a similar decay strategy to issues.
|
||||
- If the author responds, the `Needs-Author-Feedback` tag will drop.
|
||||
@@ -33,22 +33,11 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
|
||||
## Rules
|
||||
|
||||
### Triage Shorthand
|
||||
- All rules in this category apply to triaging issues. They're shorthand comments that the triage team can use in order to complete the triage process faster.
|
||||
- Only individuals with `Write` or `Admin` privileges on the repository can use these responses.
|
||||
|
||||
#### Duplicate Issues
|
||||
- When a comment on the thread says `/dup #<issue ID>`...
|
||||
1. Reply with a comment explaining that the issue is a duplicate and recommend that the opener and interested parties follow the issue on the listed ID number.
|
||||
1. Close the issue
|
||||
1. Remove all `Needs-*` tags
|
||||
1. Add `Resolution-Duplicate`
|
||||
|
||||
### Issue Management
|
||||
|
||||
#### Mark as Triage Needed
|
||||
- When an issue doesn't meet triage criteria, applies `Needs-Triage` tag. Right now, this is just when it's opened.
|
||||
|
||||
|
||||
#### Author Has Responded
|
||||
- When an issue with `Needs-Author-Feedback` gets an author response, drops that tag in favor of `Needs-Attention` to flag core contributors to drop by.
|
||||
|
||||
@@ -75,14 +64,6 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
- If an issue is filed matching a pattern that happens all the time (common duplicate phrase, obvious multiple-issues-in-one pattern)...
|
||||
- Then close the issue automatically informing the opener that they can resolve the problem and reopen the issue. (See Bug/Feature templates for example situations.)
|
||||
|
||||
#### Help ask for Feedback Hub
|
||||
- When a comment on the thread says `/feedback`...
|
||||
1. Then reply to the issue with a bit of text on asking the author to send us data with Feedback Hub and give us the link.
|
||||
1. And add the `Needs-Author-Feedback` tag
|
||||
|
||||
#### Remove Help Wanted from In PR issues
|
||||
- If an issue gets the `In-PR` tag when a new PR is created, we will remove the `Help-Wanted` tag to avoid someone trying to work on an issue where another person has already submitted a proposed fix.
|
||||
|
||||
### PR Management
|
||||
|
||||
#### Codeflow Link *(Disabled)*
|
||||
@@ -106,25 +87,16 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
#### Auto-Merge pull requests
|
||||
- When a pull request has the `AutoMerge` label...
|
||||
- If it has been at least 480 minutes and all the statuses pass, merge it in.
|
||||
- Will use Squash merge strategy
|
||||
- Will use Squash merge stratgy
|
||||
- Will attempt to delete branch after merge, if possible
|
||||
- Will automatically remove the `AutoMerge` label if changes are pushed by someone *without* Write Access.
|
||||
- More information on bot-logic that can be controlled with comments is [here](https://github.com/OfficeDev/office-ui-fabric-react/wiki/Advanced-auto-merge)
|
||||
|
||||
|
||||
#### Mark issues with an active PR
|
||||
- If there is an active PR for an issue, label that issue with the `In-PR` label
|
||||
|
||||
#### Add committed fix tag for completed PRs
|
||||
- When a PR is finished and there's no outstanding work left on a linked issue, add the `Resolution-Fix-Committed` label
|
||||
|
||||
#### Remove Needs-Second from completed PRs
|
||||
- If a PR is closed and it has the `Needs-Second` tag, the bot will remove the tag.
|
||||
|
||||
### Release Management
|
||||
|
||||
When a release is created, if the PR ID number is linked inside the release description, the bot will walk through the related PR and all of its related issues and leave a message.
|
||||
- PR message: "🎉{release name} {release version} has been released which incorporates this pull request.🎉
|
||||
- Issue message: 🎉This issue was addressed in #{pull request ID}, which has now been successfully released as {release name} {release version}.🎉"
|
||||
|
||||
|
||||
## Admin Panel
|
||||
[Here](https://fabric-cp.azurewebsites.net/bot/)
|
||||
|
||||
@@ -1,38 +1,13 @@
|
||||
|
||||
# How to build OpenConsole
|
||||
# How to build Openconsole
|
||||
|
||||
This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for some of its dependencies. To make sure submodules are restored or updated, be sure to run the following prior to building:
|
||||
|
||||
```shell
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory:
|
||||
Openconsole can be built with Visual Studio or from the command line. There are build scripts for both cmd and powershell in /tools.
|
||||
|
||||
When using Visual Studio, be sure to set up the path for code formatting. This can be done in Visual Studio by going to Tools > Options > Text Editor > C++ > Formatting and checking "Use custom clang-format.exe file" and choosing the clang-format.exe in the repository at /dep/llvm/clang-format.exe by clicking "browse" right under the check box.
|
||||
|
||||
### Building in PowerShell
|
||||
## Building with cmd
|
||||
|
||||
```powershell
|
||||
Import-Module .\tools\OpenConsole.psm1
|
||||
Set-MsBuildDevEnvironment
|
||||
Invoke-OpenConsoleBuild
|
||||
```
|
||||
|
||||
There are a few additional exported functions (look at their documentation for further details):
|
||||
|
||||
- `Invoke-OpenConsoleBuild` - builds the solution. Can be passed msbuild arguments.
|
||||
- `Invoke-OpenConsoleTests` - runs the various tests. Will run the unit tests by default.
|
||||
- `Start-OpenConsole` - starts Openconsole.exe from the output directory. x64 is run by default.
|
||||
- `Debug-OpenConsole` - starts Openconsole.exe and attaches it to the default debugger. x64 is run by default.
|
||||
- `Invoke-CodeFormat` - uses clang-format to format all c++ files to match our coding style.
|
||||
|
||||
### Building in Cmd
|
||||
|
||||
```shell
|
||||
.\tools\razzle.cmd
|
||||
bcz
|
||||
```
|
||||
The cmd scripts are set up to emulate a portion of the OS razzle build environment. razzle.cmd is the first script that should be run. bcz.cmd will build clean and bz.cmd should build incrementally.
|
||||
|
||||
There are also scripts for running the tests:
|
||||
- `runut.cmd` - run the unit tests
|
||||
@@ -40,13 +15,15 @@ There are also scripts for running the tests:
|
||||
- `runuia.cmd` - run the UIA tests
|
||||
- `runformat` - uses clang-format to format all c++ files to match our coding style.
|
||||
|
||||
## Running & Debugging
|
||||
## Build with Powershell
|
||||
|
||||
To debug the Windows Terminal in VS, right click on `CascadiaPackage` (in the Solution Explorer) and go to properties. In the Debug menu, change "Application process" and "Background task process" to "Native Only".
|
||||
Openconsole.psm1 should be loaded with `Import-Module`. From there `Set-MsbuildDevEnvironment` will set up environment variables required to build. There are a few exported functions (look at their documentation for further details):
|
||||
|
||||
You should then be able to build & debug the Terminal project by hitting <kbd>F5</kbd>.
|
||||
|
||||
> 👉 You will _not_ be able to launch the Terminal directly by running the WindowsTerminal.exe. For more details on why, see [#926](https://github.com/microsoft/terminal/issues/926), [#4043](https://github.com/microsoft/terminal/issues/4043)
|
||||
- `Invoke-OpenConsolebuild` - builds the solution. Can be passed msbuild arguments.
|
||||
- `Invoke-OpenConsoleTests` - runs the various tests. Will run the unit tests by default.
|
||||
- `Start-OpenConsole` - starts Openconsole.exe from the output directory. x64 is run by default.
|
||||
- `Debug-OpenConsole` - starts Openconsole.exe and attaches it to the default debugger. x64 is run by default.
|
||||
- `Invoke-CodeFormat` - uses clang-format to format all c++ files to match our coding style.
|
||||
|
||||
## Configuration Types
|
||||
|
||||
@@ -56,28 +33,4 @@ Openconsole has three configuration types:
|
||||
- Release
|
||||
- AuditMode
|
||||
|
||||
AuditMode is an experimental mode that enables some additional static analysis from CppCoreCheck.
|
||||
|
||||
## Updating Nuget package references
|
||||
Certain Nuget package references in this project, like `Microsoft.UI.Xaml`, must be updated outside of the Visual Studio NuGet package manager. This can be done using the snippet below.
|
||||
> Note that to run this snippet, you need to use WSL as the command uses `sed`.
|
||||
To update the version of a given package, use the following snippet
|
||||
|
||||
`git grep -z -l $PackageName | xargs -0 sed -i -e 's/$OldVersionNumber/$NewVersionNumber/g'`
|
||||
|
||||
where:
|
||||
- `$PackageName` is the name of the package, e.g. Microsoft.UI.Xaml
|
||||
- `$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.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:
|
||||
|
||||
1. Open the Nuget.config file and uncomment line 8 ("Static Package Dependencies")
|
||||
2. Create the folder /dep/packages
|
||||
3. Put your .nupkg files in /dep/packages
|
||||
4. If you are using different versions than those already being used, you need to update the references as well. How to do that is explained under "Updating Nuget package references".
|
||||
AuditMode is an experimental mode that enables some additional static analyis from CppCoreCheck.
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
# 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_.
|
||||
@@ -6,7 +6,7 @@
|
||||
## Abstract
|
||||
It should be possible to configure the terminal so that it doesn't send certain keystrokes as input to the terminal, and instead triggers certain actions. Examples of these actions could be copy/pasting text, opening a new tab, or changing the font size.
|
||||
|
||||
This spec describes a mechanism by which we could provide a common implementation of handling keyboard shortcuts like these. This common implementation could then be leveraged and extended by the UX implementation as to handle certain callbacks in the UX layer. For example, The TerminalCore doesn't have a concept of what a tab is, but the keymap abstraction could raise an event such that a WPF app could implement creating a new tab in its idiomatic way, and UWP could implement them in their own way.
|
||||
This spec describes a mechanism by which we could provide a common implementation of handling keyboard shortcuts like these. This common implementation could then be leveraged and extended by the UX implementation as to handle certain callbacks in the UX layer. For example, The TerminalCore doesn't have a concept of what a tab is, but the keymap abstraction could raise an event such that a WPF app could implement creating a new tab in its idomatic way, and UWP could implement them in their own way.
|
||||
|
||||
## Terminology
|
||||
* **Key Chord**: This is any possible keystroke that a user can input
|
||||
@@ -1,8 +1,6 @@
|
||||
---
|
||||
author: "Mike Griese @zadjii-msft"
|
||||
created on: 2019-05-16
|
||||
last updated: 2019-07-07
|
||||
issue id: 523
|
||||
created on: 2019-May-16
|
||||
---
|
||||
|
||||
# Panes in the Windows Terminal
|
||||
@@ -26,7 +24,7 @@ Windows Terminal.
|
||||
Panes within the context of a single terminal window are not a new idea. The
|
||||
design of the panes for the Windows Terminal was heavily inspired by the
|
||||
application `tmux`, which is a commandline application which acts as a "terminal
|
||||
multiplexer", allowing for the easy management of many terminal sessions from a
|
||||
multiplexer", allowing for the easy managment of many terminal sessions from a
|
||||
single application.
|
||||
|
||||
Other applications that include pane-like functionality include (but are not
|
||||
@@ -53,7 +51,7 @@ When a pane is a parent, its two children are either split vertically or
|
||||
horizontally. Parent nodes don't have a terminal of their own, they merely
|
||||
display the terminals of their children.
|
||||
|
||||
* If a Pane is split vertically, the two panes are separated by a vertical
|
||||
* If a Pane is split vertically, the two panes are seperated by a vertical
|
||||
split, as to appear side-by-side. Think `[|]`
|
||||
* If a Pane is split horizontally, the two panes are split by a horizontal
|
||||
separator, and appear above/below one another. Think `[-]`.
|
||||
@@ -115,7 +113,7 @@ We could also split `A` in horizontally, creating a fourth terminal pane `D`.
|
||||
+---------------+
|
||||
```
|
||||
|
||||
While it may appear that there's a single horizontal separator and a single
|
||||
While it may appear that there's a single horizonal separator and a single
|
||||
vertical separator here, that's not actually the case. Due to the tree-like
|
||||
structure of the pane splitting, the horizontal splits exist only between the
|
||||
two panes they're splitting. So, the user could move each of the horizontal
|
||||
@@ -228,7 +226,7 @@ pane. This could be solved a number of ways. There could be keyboard shortcuts
|
||||
for swapping the positions of tabs, or a shortcut for both "zooming" a tab
|
||||
(temporarily making it the full size) or even popping a pane out to it's own
|
||||
tab. Additionally, a right-click menu option could be added to do the
|
||||
aforementioned actions. Discoverability of these two actions is not as high as
|
||||
aformentioned actions. Discoverability of these two actions is not as high as
|
||||
just dragging a tab from one pane to another; however, it's believed that panes
|
||||
are more of a power-user scenario, and power users will not necessarily be
|
||||
are more of a power-user scenario, and power users will not neccessarily be
|
||||
turned off by the feature's discoverability.
|
||||
@@ -1,209 +1,76 @@
|
||||
# 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". |
|
||||
| `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: <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 |
|
||||
| -------- | --------- | ---- | ------- | ----------- |
|
||||
| `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. |
|
||||
| `name` | _Required_ | String | | Name of the profile. Displays in the dropdown menu. <br>Additionally, this value will be used 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. This "title" behavior can be overridden by using `tabTitle`. |
|
||||
| `acrylicOpacity` | Optional | Number | `0.5` | When `useAcrylic` is set to `true`, it sets the transparency of the window for the profile. Accepts floating point values from 0-1. |
|
||||
| `antialiasingMode` | Optional | String | `"grayscale"` | Controls how text is antialiased in the renderer. Possible values are "grayscale", "cleartype" and "aliased". Note that changing this setting will require starting a new terminal instance. |
|
||||
| `background` | Optional | String | | Sets the background color of the profile. Overrides `background` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `backgroundImage` | Optional | String | | Sets the file location of the Image to draw over the window background. |
|
||||
| `backgroundImageAlignment` | Optional | String | `center` | Sets how the background image aligns to the boundaries of the window. Possible values: `"center"`, `"left"`, `"top"`, `"right"`, `"bottom"`, `"topLeft"`, `"topRight"`, `"bottomLeft"`, `"bottomRight"` |
|
||||
| `backgroundImageOpacity` | Optional | Number | `1.0` | Sets the transparency of the background image. Accepts floating point values from 0-1. |
|
||||
| `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`. |
|
||||
| `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 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 | `"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 | `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 |
|
||||
| -------- | ---- | ----------- | ----------- |
|
||||
| `name` | _Required_ | String | Name of the color scheme. |
|
||||
| `foreground` | _Required_ | String | Sets the foreground color of the color scheme. |
|
||||
| `background` | _Required_ | String | Sets the background color of the color scheme. |
|
||||
| `selectionBackground` | Optional | String | Sets the selection background color of the color scheme. |
|
||||
| `cursorColor` | Optional | String | Sets the cursor color of the color scheme. |
|
||||
| `black` | _Required_ | String | Sets the color used as ANSI black. |
|
||||
| `blue` | _Required_ | String | Sets the color used as ANSI blue. |
|
||||
| `brightBlack` | _Required_ | String | Sets the color used as ANSI bright black. |
|
||||
| `brightBlue` | _Required_ | String | Sets the color used as ANSI bright blue. |
|
||||
| `brightCyan` | _Required_ | String | Sets the color used as ANSI bright cyan. |
|
||||
| `brightGreen` | _Required_ | String | Sets the color used as ANSI bright green. |
|
||||
| `brightPurple` | _Required_ | String | Sets the color used as ANSI bright purple. |
|
||||
| `brightRed` | _Required_ | String | Sets the color used as ANSI bright red. |
|
||||
| `brightWhite` | _Required_ | String | Sets the color used as ANSI bright white. |
|
||||
| `brightYellow` | _Required_ | String | Sets the color used as ANSI bright yellow. |
|
||||
| `cyan` | _Required_ | String | Sets the color used as ANSI cyan. |
|
||||
| `green` | _Required_ | String | Sets the color used as ANSI green. |
|
||||
| `purple` | _Required_ | String | Sets the color used as ANSI purple. |
|
||||
| `red` | _Required_ | String | Sets the color used as ANSI red. |
|
||||
| `white` | _Required_ | String | Sets the color used as ANSI white. |
|
||||
| `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] or String | Defines the key combinations used to call the command. |
|
||||
| `action` | Optional | String | Adds additional functionality to certain commands. |
|
||||
|
||||
### Implemented Commands and Actions
|
||||
|
||||
Commands listed below are per the implementation in [`src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp`](https://github.com/microsoft/terminal/blob/master/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp).
|
||||
|
||||
Keybindings can be structured in the following manners:
|
||||
|
||||
For commands without arguments:
|
||||
<br>
|
||||
`{ "command": "commandName", "keys": [ "modifiers+key" ] }`
|
||||
|
||||
For commands with arguments:
|
||||
<br>
|
||||
`{ "command": { "action": "commandName", "argument": "value" }, "keys": ["modifiers+key"] }`
|
||||
|
||||
| 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. | `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. | | | |
|
||||
| `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. | | | |
|
||||
| `openNewTabDropdown` | Open the dropdown menu. | | | |
|
||||
| `openSettings` | Open the settings file. | | | |
|
||||
| `paste` | Insert the content that was copied onto the clipboard. | | | |
|
||||
| `prevTab` | Open the tab to the left of the current one. | | | |
|
||||
| `resetFontSize` | Reset the text size to the default value. | | | |
|
||||
| `resizePane` | Change the size of the active pane. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the pane will be resized. |
|
||||
| `scrollDown` | Move the screen down. | | | |
|
||||
| `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`<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. | | | |
|
||||
|
||||
### Accepted Modifiers and Keys
|
||||
|
||||
#### Modifiers
|
||||
`ctrl+`, `shift+`, `alt+`
|
||||
|
||||
#### Keys
|
||||
|
||||
| Type | Keys |
|
||||
| ---- | ---- |
|
||||
| Function and Alphanumeric Keys | `f1-f24`, `a-z`, `0-9` |
|
||||
| Symbols | ``` ` ```, `-`, `=`, `[`, `]`, `\`, `;`, `'`, `,`, `.`, `/` |
|
||||
| Arrow Keys | `down`, `left`, `right`, `up`, `pagedown`, `pageup`, `pgdn`, `pgup`, `end`, `home`, `plus` |
|
||||
| Action Keys | `tab`, `enter`, `esc`, `escape`, `space`, `backspace`, `delete`, `insert` |
|
||||
| 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:
|
||||
|
||||
| URI Scheme | Corresponding Physical Path | Use / description |
|
||||
| --- | --- | ---|
|
||||
| `ms-appdata:///Local/` | `%localappdata%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\` | Per-machine files |
|
||||
| `ms-appdata:///Roaming/` | `%localappdata%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\RoamingState\` | Common files |
|
||||
|
||||
> ⚠ 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.
|
||||
|
||||
You can refer to you own icons if you wish, e.g.:
|
||||
|
||||
```json
|
||||
"icon" : "C:\\Users\\richturn\\OneDrive\\WindowsTerminal\\icon-ubuntu-32.png",
|
||||
```
|
||||
|
||||
> 👉 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`.
|
||||
|
||||
For example:
|
||||
```json
|
||||
"backgroundImage": "C:\\Users\\richturn\\OneDrive\\WindowsTerminal\\bg-ubuntu-256.png",
|
||||
"backgroundImageAlignment": "bottomRight",
|
||||
"backgroundImageOpacity": 0.1,
|
||||
"backgroundImageStretchMode": "none"
|
||||
```
|
||||
|
||||
> 👉 Tip: You can easily roam your collection of images and icons across all your machines by storing your icons and images in OneDrive (as shown above).
|
||||
|
||||
With these settings, your Terminal's Ubuntu profile would look similar to this:
|
||||
|
||||

|
||||
# Profiles.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>. |
|
||||
| `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. |
|
||||
| `initialRows` | _Required_ | Integer | `30` | The number of rows displayed in the window upon first load. |
|
||||
| `requestedTheme` | _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. |
|
||||
| `wordDelimiters` | Optional | String | ` ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?\u2502` | Determines the delimiters used in a double click selection. |
|
||||
|
||||
## Profiles
|
||||
Properties listed below are specific to each unique profile.
|
||||
|
||||
| Property | Necessity | Type | Default | Description |
|
||||
| -------- | --------- | ---- | ------- | ----------- |
|
||||
| `acrylicOpacity` | _Required_ | Number | `0.5` | When `useAcrylic` is set to `true`, it sets the transparency of the window for the profile. Accepts floating point values from 0-1. |
|
||||
| `closeOnExit` | _Required_ | Boolean | `true` | When set to `true`, the selected tab closes when `exit` is typed. When set to `false`, the tab will remain open when `exit` is typed. |
|
||||
| `colorScheme` | _Required_ | String | `Campbell` | Name of the terminal color scheme to use. Color schemes are defined under `schemes`. |
|
||||
| `commandline` | _Required_ | String | `powershell.exe` | Executable used in the profile. |
|
||||
| `cursorColor` | _Required_ | String | `#FFFFFF` | Sets the cursor color for the profile. Uses hex color format: `"#rrggbb"`. |
|
||||
| `cursorShape` | _Required_ | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( ▃ ), `"bar"` ( ┃ ), `"underscore"` ( ▁ ), `"filledBox"` ( █ ), `"emptyBox"` ( ▯ ) |
|
||||
| `fontFace` | _Required_ | String | `Consolas` | Name of the font face used in the profile. |
|
||||
| `fontSize` | _Required_ | Integer | `10` | Sets the font size. |
|
||||
| `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. |
|
||||
| `historySize` | _Required_ | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. |
|
||||
| `name` | _Required_ | String | `PowerShell Core` | Name of the profile. Displays in the dropdown menu. |
|
||||
| `padding` | _Required_ | String | `0, 0, 0, 0` | 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. |
|
||||
| `snapOnInput` | _Required_ | 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. |
|
||||
| `startingDirectory` | _Required_ | String | `%USERPROFILE%` | The directory the shell starts in when it is loaded. |
|
||||
| `useAcrylic` | _Required_ | 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. |
|
||||
| `background` | Optional | String | | Sets the background color of the profile. Overrides `background` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `colorTable` | Optional | Array[String] | | 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]` |
|
||||
| `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. |
|
||||
| `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. |
|
||||
| `scrollbarState` | Optional | String | | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` |
|
||||
| `tabTitle` | Optional | String | | Overrides default title of the tab. |
|
||||
|
||||
## 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 |
|
||||
| -------- | ---- | ----------- | ----------- |
|
||||
| `name` | _Required_ | String | Name of the color scheme. |
|
||||
| `foreground` | _Required_ | String | Sets the foreground color of the color scheme. |
|
||||
| `background` | _Required_ | String | Sets the background color of the color scheme. |
|
||||
| `black` | _Required_ | String | Sets the color used as ANSI black. |
|
||||
| `blue` | _Required_ | String | Sets the color used as ANSI blue. |
|
||||
| `brightBlack` | _Required_ | String | Sets the color used as ANSI bright black. |
|
||||
| `brightBlue` | _Required_ | String | Sets the color used as ANSI bright blue. |
|
||||
| `brightCyan` | _Required_ | String | Sets the color used as ANSI bright cyan. |
|
||||
| `brightGreen` | _Required_ | String | Sets the color used as ANSI bright green. |
|
||||
| `brightPurple` | _Required_ | String | Sets the color used as ANSI bright purple. |
|
||||
| `brightRed` | _Required_ | String | Sets the color used as ANSI bright red. |
|
||||
| `brightWhite` | _Required_ | String | Sets the color used as ANSI bright white. |
|
||||
| `brightYellow` | _Required_ | String | Sets the color used as ANSI bright yellow. |
|
||||
| `cyan` | _Required_ | String | Sets the color used as ANSI cyan. |
|
||||
| `green` | _Required_ | String | Sets the color used as ANSI green. |
|
||||
| `purple` | _Required_ | String | Sets the color used as ANSI purple. |
|
||||
| `red` | _Required_ | String | Sets the color used as ANSI red. |
|
||||
| `white` | _Required_ | String | Sets the color used as ANSI white. |
|
||||
| `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. |
|
||||
|
||||
@@ -33,7 +33,7 @@ This spec will outline how various terminal frontends will be able to interact w
|
||||
5. Visual Studio should be able to persist and edit settings globally, without
|
||||
the need for a globals/profiles structure.
|
||||
6. The Terminal should be able to read information from a settings structure
|
||||
that's independent of how it's persisted / implemented by the Application
|
||||
that's independant of how it's persisted / implemented by the Application
|
||||
7. The Component should be able to have its own settings independent of the
|
||||
application that's embedding it, such as font size and face, scrollbar
|
||||
visibility, etc. These should be settings that are specific to the component,
|
||||
@@ -79,7 +79,7 @@ Shell Commandline |
|
||||
|
||||
### Simple Settings
|
||||
|
||||
An application like VS might not even care about settings profiles. They should be able to persist the settings as just a singular entity, and change those as needed, without the additional overhead. Profiles will be something that's more specific to Project Cascadia.
|
||||
An application like VS might not even care about settings profiles. They should be able to persist the settings as just a singular entity, and change those as needed, without the additional overhead. Profiles will be something that's more specifc to Project Cascadia.
|
||||
|
||||
### Interface Descriptions
|
||||
|
||||
@@ -228,6 +228,6 @@ I don't like that - if we change the font size, we should just recalculate how m
|
||||
## Questions / TODO
|
||||
* How does this interplay with setting properties of the terminal component in XAML?
|
||||
* I would think that the component would load the XAML properties first, and if the controlling application calls `UpdateSettings` on the component, then those in-XAML properties would likely get overwritten.
|
||||
* It's not necessary to create the component with a `IComponentSettings`, nor is it necessary to call `UpdateSettings`. If you wanted to create a trivial settings-less terminal component entirely in XAML, go right ahead.
|
||||
* It's not necessary to create the component with a `IComponentSettings`, nor is it necessary to call `UpdateSettings`. If you wanted to create a trivial settings-less terminal component entriely in XAML, go right ahead.
|
||||
* Any settings that *are* exposed through XAML properties *should* also be exposed in the component's settings implementation as well.
|
||||
* Can that be enforced any way? I doubt it.
|
||||
@@ -68,7 +68,7 @@ original files. You could alternatively put all the source in one directory, and
|
||||
have separate `dll/` and `lib/` subdirectories from the source that are solely
|
||||
responsible for building their binary.
|
||||
|
||||
At this point, you might face some difficulty including the right winmd
|
||||
At this point, you might face some difficulty including the right wimnd
|
||||
references, especially from other C++/WinRT dependencies for this project that
|
||||
exist in your solution. I don't know why, but I had a fair amount of difficulty
|
||||
using a `ProjectReference` from a C++/WinRT StaticLibrary to another C++/WinRT
|
||||
@@ -82,7 +82,7 @@ project from our `TerminalAppLib` project:
|
||||
<ItemGroup>
|
||||
<!-- Manually add references to each of our dependent winmds. Mark them as
|
||||
private=false and CopyLocalSatelliteAssemblies=false, so that we don't
|
||||
propagate them upwards (which can make referencing this project result in
|
||||
propogate them upwards (which can make referencing this project result in
|
||||
duplicate type definitions)-->
|
||||
|
||||
<Reference Include="Microsoft.Terminal.Settings">
|
||||
@@ -106,7 +106,7 @@ in the dll project's directory.
|
||||
|
||||
### Update the dll project
|
||||
|
||||
Now that we have a lib that builds all your code, we can go ahead and tear out
|
||||
Now that we havea lib that builds all your code, we can go ahead and tear out
|
||||
most of the dead code from the old dll project. Remove all the source files from
|
||||
the dll's `.vcxproj` file, save for the `pch.h` and `pch.cpp` files. You _may_
|
||||
need to leave the headers for any C++/WinRT types you've authored in this project
|
||||
@@ -301,7 +301,7 @@ you want to use any XAML types, then you'll have to keep reading.
|
||||
|
||||
### Using Xaml Types (with XAML Islands)
|
||||
|
||||
To be able to instantiate XAML types in your unittest, we'll need to make use of
|
||||
To be able to instatiate XAML types in your unittest, we'll need to make use of
|
||||
the [XAML Hosting
|
||||
API](https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/using-the-xaml-hosting-api)
|
||||
(Xaml Islands). This enables you to use XAML APIs from a Win32 context.
|
||||
|
||||
@@ -1,817 +0,0 @@
|
||||
{
|
||||
"$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",
|
||||
"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)$",
|
||||
"type": "string",
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+]<keyName>\", where each modifier is optional, separated by + symbols, and keyName is either one of the names listed in the table below, or any single key character. The string should be written in full lowercase.\nbackspace\tBACKSPACE key\ntab\tTAB key\nenter\tENTER key\nesc, escape\tESC key\nspace\tSPACEBAR\npgup, pageup\tPAGE UP key\npgdn, pagedown\tPAGE DOWN key\nend\tEND key\nhome\tHOME key\nleft\tLEFT ARROW key\nup\tUP ARROW key\nright\tRIGHT ARROW key\ndown\tDOWN ARROW key\ninsert\tINS key\ndelete\tDEL key\nnumpad_0-numpad_9, numpad0-numpad9\tNumeric keypad keys 0 to 9. Can't be combined with the shift modifier.\nnumpad_multiply\tNumeric keypad MULTIPLY key (*)\nnumpad_plus, numpad_add\tNumeric keypad ADD key (+)\nnumpad_minus, numpad_subtract\tNumeric keypad SUBTRACT key (-)\nnumpad_period, numpad_decimal\tNumeric keypad DECIMAL key (.). Can't be combined with the shift modifier.\nnumpad_divide\tNumeric keypad DIVIDE key (/)\nf1-f24\tF1 to F24 function keys\nplus\tADD key (+)"
|
||||
},
|
||||
"Color": {
|
||||
"default": "#",
|
||||
"pattern": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
|
||||
"type": "string",
|
||||
"format": "color"
|
||||
},
|
||||
"Coordinates": {
|
||||
"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}\\}$",
|
||||
"type": "string"
|
||||
},
|
||||
"ShortcutActionName": {
|
||||
"enum": [
|
||||
"adjustFontSize",
|
||||
"closePane",
|
||||
"closeTab",
|
||||
"closeWindow",
|
||||
"copy",
|
||||
"duplicateTab",
|
||||
"moveFocus",
|
||||
"newTab",
|
||||
"nextTab",
|
||||
"openNewTabDropdown",
|
||||
"openSettings",
|
||||
"paste",
|
||||
"prevTab",
|
||||
"resetFontSize",
|
||||
"resizePane",
|
||||
"scrollDown",
|
||||
"scrollDownPage",
|
||||
"scrollUp",
|
||||
"scrollUpPage",
|
||||
"splitPane",
|
||||
"switchToTab",
|
||||
"toggleFullscreen",
|
||||
"find",
|
||||
"unbound"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"Direction": {
|
||||
"enum": [
|
||||
"left",
|
||||
"right",
|
||||
"up",
|
||||
"down"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SplitState": {
|
||||
"enum": [
|
||||
"vertical",
|
||||
"horizontal",
|
||||
"auto"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"NewTerminalArgs": {
|
||||
"properties": {
|
||||
"commandline": {
|
||||
"description": "A commandline to use instead of the profile's",
|
||||
"type": "string"
|
||||
},
|
||||
"tabTitle": {
|
||||
"description": "An initial tabTitle to use instead of the profile's",
|
||||
"type": "string"
|
||||
},
|
||||
"startingDirectory": {
|
||||
"description": "A startingDirectory to use instead of the profile's",
|
||||
"type": "string"
|
||||
},
|
||||
"profile": {
|
||||
"description": "Either the GUID or name of a profile to use, instead of launching the default",
|
||||
"type": "string"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"description": "The index of the profile in the new tab dropdown (starting at 0)"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ShortcutAction": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "The action to execute",
|
||||
"$ref": "#/definitions/ShortcutActionName"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"action"
|
||||
],
|
||||
"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": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "copy" },
|
||||
"singleLine": {
|
||||
"type": "boolean",
|
||||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"NewTabAction": {
|
||||
"description": "Arguments corresponding to a New Tab Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{ "$ref": "#/definitions/NewTerminalArgs" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type":"string", "pattern": "newTab" }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"SwitchToTabAction": {
|
||||
"description": "Arguments corresponding to a Switch To Tab Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "switchToTab" },
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Which tab to switch to, with the first being 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "index" ]
|
||||
},
|
||||
"MoveFocusAction": {
|
||||
"description": "Arguments corresponding to a Move Focus Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "moveFocus" },
|
||||
"direction": {
|
||||
"$ref": "#/definitions/Direction",
|
||||
"default": "left",
|
||||
"description": "The direction to move focus in, between panes"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"ResizePaneAction": {
|
||||
"description": "Arguments corresponding to a Resize Pane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "resizePane" },
|
||||
"direction": {
|
||||
"$ref": "#/definitions/Direction",
|
||||
"default": "left",
|
||||
"description": "The direction to move the pane separator in"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"SplitPaneAction": {
|
||||
"description": "Arguments corresponding to a Split Pane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{ "$ref": "#/definitions/NewTerminalArgs" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "splitPane" },
|
||||
"split": {
|
||||
"$ref": "#/definitions/SplitState",
|
||||
"default": "auto",
|
||||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"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,
|
||||
"properties": {
|
||||
"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/OpenSettingsAction" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
"keys": {
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"$ref": "#/definitions/KeyChordSegment"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"command",
|
||||
"keys"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Globals": {
|
||||
"additionalProperties": true,
|
||||
"description": "Properties that affect the entire window, regardless of the profile settings.",
|
||||
"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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyOnSelect": {
|
||||
"default": false,
|
||||
"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": {
|
||||
"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,
|
||||
"description": "The number of columns displayed in the window upon first load.",
|
||||
"maximum": 999,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"initialRows": {
|
||||
"default": 30,
|
||||
"description": "The number of rows displayed in the window upon first load.",
|
||||
"maximum": 999,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"launchMode": {
|
||||
"default": "default",
|
||||
"description": "Defines whether the Terminal will launch as maximized or not.",
|
||||
"enum": [
|
||||
"maximized",
|
||||
"default"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"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\".",
|
||||
"maximum": 999,
|
||||
"minimum": 0,
|
||||
"type": [ "integer", "string" ]
|
||||
},
|
||||
"keybindings": {
|
||||
"description": "Properties are specific to each custom key binding.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Keybinding"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"theme": {
|
||||
"default": "system",
|
||||
"description": "Sets the theme of the application. The special value \"system\" refers to the active Windows system theme.",
|
||||
"enum": [
|
||||
"light",
|
||||
"dark",
|
||||
"system"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"showTabsInTitlebar": {
|
||||
"default": true,
|
||||
"description": "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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"showTerminalTitleInTitlebar": {
|
||||
"default": true,
|
||||
"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",
|
||||
"type": "boolean"
|
||||
},
|
||||
"tabWidthMode": {
|
||||
"default": "equal",
|
||||
"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"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"wordDelimiters": {
|
||||
"default": " ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?│",
|
||||
"description": "Determines the delimiters used in a double click selection.",
|
||||
"type": "string"
|
||||
},
|
||||
"confirmCloseAllTabs": {
|
||||
"default": true,
|
||||
"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": [
|
||||
"defaultProfile"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Profile": {
|
||||
"description": "Properties specific to a unique profile.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"acrylicOpacity": {
|
||||
"default": 0.5,
|
||||
"description": "When useAcrylic is set to true, it sets the transparency of the window for the profile. Accepts floating point values from 0-1 (default 0.5).",
|
||||
"maximum": 1,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
},
|
||||
"antialiasingMode": {
|
||||
"default": "grayscale",
|
||||
"description": "Controls how text is antialiased in the renderer. Possible values are \"grayscale\", \"cleartype\" and \"aliased\". Note that changing this setting will require starting a new terminal instance.",
|
||||
"enum": [
|
||||
"grayscale",
|
||||
"cleartype",
|
||||
"aliased"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"background": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": "#0c0c0c",
|
||||
"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", "null"]
|
||||
},
|
||||
"backgroundImageAlignment": {
|
||||
"default": "center",
|
||||
"enum": [
|
||||
"bottom",
|
||||
"bottomLeft",
|
||||
"bottomRight",
|
||||
"center",
|
||||
"left",
|
||||
"right",
|
||||
"top",
|
||||
"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": {
|
||||
"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": {
|
||||
"default": "uniformToFill",
|
||||
"description": "Sets how the background image is resized to fill the window.",
|
||||
"enum": [
|
||||
"fill",
|
||||
"none",
|
||||
"uniform",
|
||||
"uniformToFill"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"closeOnExit": {
|
||||
"default": "graceful",
|
||||
"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": [
|
||||
"never",
|
||||
"graceful",
|
||||
"always"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"colorScheme": {
|
||||
"default": "Campbell",
|
||||
"description": "Name of the terminal color scheme to use. Color schemes are defined under \"schemes\".",
|
||||
"type": "string"
|
||||
},
|
||||
"commandline": {
|
||||
"description": "Executable used in the profile.",
|
||||
"type": "string"
|
||||
},
|
||||
"cursorColor": {
|
||||
"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","null"],
|
||||
"default": 25
|
||||
},
|
||||
"cursorShape": {
|
||||
"default": "bar",
|
||||
"description": "Sets the shape of the cursor. Possible values:\n -\"bar\" ( ┃, default )\n -\"emptyBox\" ( ▯ )\n -\"filledBox\" ( █ )\n -\"underscore\" ( ▁ )\n -\"vintage\" ( ▃ )",
|
||||
"enum": [
|
||||
"bar",
|
||||
"emptyBox",
|
||||
"filledBox",
|
||||
"underscore",
|
||||
"vintage"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"experimental.retroTerminalEffect": {
|
||||
"description": "When set to true, enable retro terminal effects. This is an experimental feature, and its continued existence is not guaranteed.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"fontFace": {
|
||||
"default": "Cascadia Mono",
|
||||
"description": "Name of the font face used in the profile.",
|
||||
"type": "string"
|
||||
},
|
||||
"fontSize": {
|
||||
"default": 12,
|
||||
"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 text color. Overrides \"foreground\" from the color scheme. Uses hex color format: \"#rrggbb\".",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"guid": {
|
||||
"$ref": "#/definitions/ProfileGuid",
|
||||
"description": "Unique identifier of the profile. Written in registry format: \"{00000000-0000-0000-0000-000000000000}\"."
|
||||
},
|
||||
"hidden": {
|
||||
"default": false,
|
||||
"description": "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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"historySize": {
|
||||
"default": 9001,
|
||||
"description": "The number of lines above the ones displayed in the window you can scroll back to.",
|
||||
"minimum": -1,
|
||||
"type": "integer"
|
||||
},
|
||||
"icon": {
|
||||
"description": "Image file location of the icon used in the profile. Displays within the tab and the dropdown menu.",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of the profile. Displays in the dropdown menu.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"padding": {
|
||||
"default": "8, 8, 8, 8",
|
||||
"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"
|
||||
},
|
||||
"scrollbarState": {
|
||||
"default": "visible",
|
||||
"description": "Defines the visibility of the scrollbar.",
|
||||
"enum": [
|
||||
"visible",
|
||||
"hidden"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"selectionBackground": {
|
||||
"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", "null"]
|
||||
},
|
||||
"startingDirectory": {
|
||||
"description": "The directory the shell starts in when it is loaded.",
|
||||
"type": "string"
|
||||
},
|
||||
"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",
|
||||
"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", "null"]
|
||||
},
|
||||
"useAcrylic": {
|
||||
"default": false,
|
||||
"description": "When set to true, the window will have an acrylic background. When set to false, the window will have a plain, untextured background.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ProfileList": {
|
||||
"description": "A list of profiles and the properties specific to each.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Profile",
|
||||
"required": [
|
||||
"guid",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"ProfilesObject": {
|
||||
"description": "A list of profiles and default settings that apply to all of them",
|
||||
"properties": {
|
||||
"list": {
|
||||
"$ref": "#/definitions/ProfileList"
|
||||
},
|
||||
"defaults": {
|
||||
"description": "The default settings that apply to every profile.",
|
||||
"$ref": "#/definitions/Profile"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"SchemeList": {
|
||||
"description": "Properties are specific to each color scheme. ColorTool is a great tool you can use to create and explore new color schemes. All colors use hex color format.",
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Name of the color scheme.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"background": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the background color of the color scheme."
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"cursorColor": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": "#FFFFFF",
|
||||
"description": "Sets the cursor color of the color scheme."
|
||||
},
|
||||
"cyan": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI cyan."
|
||||
},
|
||||
"foreground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the foreground color of the color scheme."
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"selectionBackground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the selection background color of the color scheme."
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/Globals" },
|
||||
{
|
||||
"additionalItems": true,
|
||||
"properties": {
|
||||
"profiles": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/ProfileList" },
|
||||
{ "$ref": "#/definitions/ProfilesObject" }
|
||||
]
|
||||
},
|
||||
"schemes": { "$ref": "#/definitions/SchemeList" }
|
||||
},
|
||||
"required": [
|
||||
"profiles",
|
||||
"schemes",
|
||||
"defaultProfile"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,9 +21,6 @@ We drive the bot by tagging issues with specific labels which cause the bot engi
|
||||
Therefore, if you do file issues, or create PRs, please keep an eye on your GitHub notifications. If you do not respond to requests for information, your issues/PRs may be closed automatically.
|
||||
|
||||
---
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC). See [SECURITY.md](./SECURITY.md) for more information.
|
||||
|
||||
## Before you start, file an issue
|
||||
|
||||
@@ -125,7 +122,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%20Wanted).
|
||||
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).
|
||||
|
||||
---
|
||||
|
||||
@@ -155,4 +152,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%20Wanted)? 😜
|
||||
Thank you in advance for your contribution! Now, [what's next on the list](https://github.com/microsoft/terminal/labels/Help-Wanted)? 😜
|
||||
|
Before Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 110 KiB |
@@ -1,100 +0,0 @@
|
||||
---
|
||||
author: Kaiyu Wang KaiyuWang16/kawa@microsoft.com
|
||||
created on: 2019-09-03
|
||||
last updated: 2020-01-02
|
||||
issue id: #1043
|
||||
---
|
||||
|
||||
# Set the initial position for terminal
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec is for task #1043 “Be able to set an initial position for the terminal”. It goes over the details of a new feature that allows users to set the initial position and size of the terminal. Expected behavior and design of this feature is included. Besides, future possible follow-up works are also addressed.
|
||||
|
||||
## Inspiration
|
||||
|
||||
The idea is to allow users to set the initial position of the Terminal when they launch it, prevent the Terminal from appearing on unexpected position (e.g. outside of the screen bounds). We are also going to let users choose to maximize the window when they launch it.
|
||||
|
||||
## Solution Design
|
||||
|
||||
For now, the Terminal window is put on a default initial position. The program uses CW_USEDEFAULT in the screen coordinates for top-left corner. We have two different types of window – client window and non-client window. However, code path for window creation (WM_CREATE message is shared by the two types of windows) are almost the same for the two types of windows, except that there are some differences in calculation of the width and height of the window.
|
||||
|
||||
Two new properties should be added in the json settings file:
|
||||
|
||||
**initialPosition**: string. This sets the initial horizontal and vertical position of the top-left corner of the window. This property follows a structure: "X value, Y value" and has following rules:
|
||||
|
||||
1. All spaces will be ignored.
|
||||
|
||||
2. Both X value and Y values are optional. If anyone of them is missing, or the value is invalid, system default value will be used. Examples:
|
||||
|
||||
", 1000" equals to (default, 1000)
|
||||
"1000, " equals to (1000, default)
|
||||
"," equals to (default, default)
|
||||
"abc, 1000" equals to (default, 1000)
|
||||
|
||||
**launchMode**: string. Determine the launch mode. There are two modes for now
|
||||
|
||||
1. maximize: the window will be maximized when launch.
|
||||
2. default: the window will be initialized with system default size.
|
||||
|
||||
The steps of this process:
|
||||
|
||||
1. Set the top-left origin, width and height to CW_USEDEFAULT.
|
||||
2. Get the dpi of the nearest monitor; Load settings.
|
||||
3. From settings, find the user-defined initial position and launch mode.
|
||||
4. If the user sets custom initial position, calculate the new position considering the current dpi and monitor. If not, use system default value.
|
||||
5. If the user set launch mode as "maximize", calculate the new height and width. If the user choose "default", use system default size.
|
||||
6. SetWindowPos with the new position and dimension of the window.
|
||||
|
||||
Step 2 to 6 should be done in `AppHost::_HandleCreateWindow`, which is consistent to the current code.
|
||||
|
||||
In step 4, we may need to consider the dpi of the current monitor and multi-monitor scenario when calculating the initial position of the window.
|
||||
|
||||
Edge cases:
|
||||
|
||||
1. Multiple monitors. The user should be able to set the initial position to any monitors attached. For the monitors on the left side of the major monitor, the initial position values are negative.
|
||||
2. If the initial position is larger than the screen resolution and the window top left corner is off-screen, we should let user be able to see and drag the window back on screen. One solution is to set the initial position to the top left corner of the nearest monitor if the top left is off-screen.
|
||||
3. If the user wants to launch maximized and provides an initial position, we should launch the maximized window on the top left corner of the monitor where the position is located.
|
||||
4. Launch the Terminal on a monitor with custom dpi. Changing the dpi of the monitor will not affect the initial position of the top left corner. So we do not need to handle this case.
|
||||
5. Launch the Terminal on a monitor with custom resolution. Changing the resolution will change the available point for the initial position. (2) already covers this case.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
Upon successful implementation, the user is able to add new properties to the json profile file, which is illustrated in the code block below:
|
||||
```json
|
||||
"initialPosition": "500,500",
|
||||
"launchMode": "default"
|
||||
```
|
||||
The rest of the UI will be the same of the current Terminal experience, except that the initial position may be different.
|
||||
|
||||
### Accessibility
|
||||
|
||||
Users can only set the initial position and launch mode in the Json file with keyboard. Thus, this will not affect accessibility.
|
||||
|
||||
### Reliability
|
||||
We need to make sure that whatever the initial position is set, the user can access the Terminal window. This is guaranteed because if the top left corner position of the Terminal Window is out of screen, we put it on the top left corner of the screen.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
More data reading and calculation will be included in Terminal Launch process, which may inversely influence the launch time. However, the impact is trivial.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
We need to consider multi-monitor scenario. If the user has multiple monitors, we must guarantee that the Terminal could be initialized as expected. We can keep an eye on the feedbacks of this feature from the community.
|
||||
|
||||
## Future considerations
|
||||
|
||||
For now, this feature only allows the user to set initial position and choose whether to maximize the window when launch. In the future, we may consider follow-up features like:
|
||||
|
||||
1. Save the position of the Terminal on exit, and restore the position on the next launch. This could be a true/false feature that users could choose to set.
|
||||
|
||||
2. We may need to consider multiple Terminal windows scenario. If the user opens multiple Terminal windows, then we need to consider how to save and restore the position.
|
||||
|
||||
3. We may also consider more launch modes. Like full screen mode and minimized mode.
|
||||
|
||||
Github issue for future follow-ups: https://github.com/microsoft/terminal/issues/766
|
||||
|
||||
## Resources
|
||||
|
||||
Github issue:
|
||||
https://github.com/microsoft/terminal/issues/1043
|
||||
@@ -1,362 +0,0 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2019-06-19
|
||||
last updated: 2019-07-14
|
||||
issue id: 1142
|
||||
---
|
||||
|
||||
# Arbitrary Keybindings Arguments
|
||||
|
||||
## Abstract
|
||||
|
||||
The goal of this change is to both simplify the keybindings, and also enable far
|
||||
more flexibility when editing a user's keybindings.
|
||||
|
||||
Currently, we have many actions that are very similar in implementation - for
|
||||
example, `newTabProfile0`, `newTabProfile1`, `newTabProfile2`, etc. All these
|
||||
actions are _fundamentally_ the same function. However, we've needed to define 9
|
||||
different actions to enable the user to provide different values to the `newTab`
|
||||
function.
|
||||
|
||||
With this change, we'll be able to remove these _essentially_ duplicated events,
|
||||
and allow the user to specify arbitrary arguments to these functions.
|
||||
|
||||
## Inspiration
|
||||
|
||||
Largely inspired by the keybindings in VsCode and Sublime Text. Additionally,
|
||||
much of the content regarding keybinding events being "handled" was designed as
|
||||
a solution for [#2285].
|
||||
|
||||
## Solution Design
|
||||
|
||||
We'll need to introduce args to some actions that we already have defined. These
|
||||
are the actions I'm thinking about when writing this spec:
|
||||
|
||||
```csharp
|
||||
// These events already exist like this:
|
||||
delegate void NewTabWithProfileEventArgs(Int32 profileIndex);
|
||||
delegate void SwitchToTabEventArgs(Int32 profileIndex);
|
||||
delegate void ResizePaneEventArgs(Direction direction);
|
||||
delegate void MoveFocusEventArgs(Direction direction);
|
||||
|
||||
// These events either exist in another form or don't exist.
|
||||
delegate void CopyTextEventArgs(Boolean copyWhitespace);
|
||||
delegate void ScrollEventArgs(Int32 numLines);
|
||||
delegate void SplitProfileEventArgs(Orientation splitOrientation, Int32 profileIndex);
|
||||
```
|
||||
|
||||
Ideally, after this change, the bindings for these actions would look something
|
||||
like the following:
|
||||
|
||||
```js
|
||||
{ "keys": ["ctrl+shift+1"], "command": "newTabProfile", "args": { "profileIndex":0 } },
|
||||
{ "keys": ["ctrl+shift+2"], "command": "newTabProfile", "args": { "profileIndex":1 } },
|
||||
// etc...
|
||||
|
||||
{ "keys": ["alt+1"], "command": "switchToTab", "args": { "index":0 } },
|
||||
{ "keys": ["alt+2"], "command": "switchToTab", "args": { "index":1 } },
|
||||
// etc...
|
||||
|
||||
{ "keys": ["alt+shift+down"], "command": "resizePane", "args": { "direction":"down" } },
|
||||
{ "keys": ["alt+shift+up"], "command": "resizePane", "args": { "direction":"up" } },
|
||||
// etc...
|
||||
|
||||
{ "keys": ["alt+down"], "command": "moveFocus", "args": { "direction":"down" } },
|
||||
{ "keys": ["alt+up"], "command": "moveFocus", "args": { "direction":"up" } },
|
||||
// etc...
|
||||
|
||||
{ "keys": ["ctrl+c"], "command": "copy", "args": { "copyWhitespace":true } },
|
||||
{ "keys": ["ctrl+shift+c"], "command": "copy", "args": { "copyWhitespace":false } },
|
||||
|
||||
{ "keys": ["ctrl+shift+down"], "command": "scroll", "args": { "numLines":1 } },
|
||||
{ "keys": ["ctrl+shift+up"], "command": "scroll", "args": { "numLines":-1 } },
|
||||
|
||||
{ "keys": ["ctrl+alt+1"], "command": "splitProfile", "args": { "orientation":"vertical", "profileIndex": 0 } },
|
||||
{ "keys": ["ctrl+alt+shift+1"], "command": "splitProfile", "args": { "orientation":"horizontal", "profileIndex": 0 } },
|
||||
{ "keys": ["ctrl+alt+2"], "command": "splitProfile", "args": { "orientation":"vertical", "profileIndex": 1 } },
|
||||
{ "keys": ["ctrl+alt+shift+2"], "command": "splitProfile", "args": { "orientation":"horizontal", "profileIndex": 1 } },
|
||||
// etc...
|
||||
```
|
||||
|
||||
Note that instead of having 9 different `newTabProfile<N>` actions, we have a
|
||||
singular `newTabProfile` action, and that action requires a `profileIndex` in
|
||||
the `args` object.
|
||||
|
||||
Also, pay attention to the last set of keybindings, the `splitProfile` ones.
|
||||
This is a function that requires two arguments, both an `orientation` and a
|
||||
`profileIndex`. Before this change we would have needed to create 20 separate
|
||||
actions (10 profile indices * 2 directions) to handle these cases. Now it can
|
||||
be done with a single action that can be much more flexible in its
|
||||
implementation.
|
||||
|
||||
### Parsing KeyBinding Arguments
|
||||
|
||||
We'll add two new interfaces: `IActionArgs` and `IActionEventArgs`. Classes that
|
||||
implement `IActionArgs` will contain all the per-action args, like
|
||||
`CopyWhitespace` or `ProfileIndex`. `IActionArgs` by itself will be an empty
|
||||
interface, but all other arguments will derive from it. `IActionEventArgs` will
|
||||
have a single property `Handled`, which will be used for indicating if a
|
||||
particular event was processed or not. When parsing args, we'll build
|
||||
`IActionArgs` to contain all the parameters. When dispatching events, we'll
|
||||
build `IActionEventArgs` using the `IActionArgs` to set all the parameter values.
|
||||
|
||||
All current keybinding events will be changed from their current types to
|
||||
`TypedEventHandler`s. These `TypedEventHandler`s second param will always be an
|
||||
instance of `IActionEventArgs`. So, for example:
|
||||
|
||||
```csharp
|
||||
|
||||
delegate void CopyTextEventArgs();
|
||||
delegate void NewTabEventArgs();
|
||||
delegate void NewTabWithProfileEventArgs(Int32 profileIndex);
|
||||
// ...
|
||||
|
||||
[default_interface]
|
||||
runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings
|
||||
{
|
||||
event CopyTextEventArgs CopyText;
|
||||
event NewTabEventArgs NewTab;
|
||||
event NewTabWithProfileEventArgs NewTabWithProfile;
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```csharp
|
||||
interface IActionArgs { /* Empty */ }
|
||||
|
||||
runtimeclass ActionEventArgs
|
||||
{
|
||||
Boolean Handled;
|
||||
ActionArgs Args;
|
||||
}
|
||||
|
||||
runtimeclass CopyTextArgs : IActionArgs
|
||||
{
|
||||
Boolean CopyWhitespace;
|
||||
}
|
||||
|
||||
runtimeclass NewTabWithProfileArgs : IActionArgs
|
||||
{
|
||||
Int32 ProfileIndex;
|
||||
}
|
||||
runtimeclass NewTabWithProfileEventArgs : NewTabWithProfileArgs, IActionArgs { }
|
||||
|
||||
[default_interface]
|
||||
runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings
|
||||
{
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> CopyText;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> NewTab;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> NewTabWithProfile;
|
||||
```
|
||||
|
||||
In this above example, the `CopyTextArgs` class actually contains all the
|
||||
potential arguments to the Copy action. `ActionEventArgs` is the class that
|
||||
holds any `ActionArgs`. When we parse the arguments, we'll build a
|
||||
`CopyTextArgs`, and when we're dispatching the event, we'll build a
|
||||
`ActionEventArgs` that holds a `CopyTextArgs` as its `Args` value, and dispatch
|
||||
the `ActionEventArgs` object.
|
||||
|
||||
|
||||
We'll also change our existing map in the `AppKeyBindings` implementation.
|
||||
Currently, it's a `std::unordered_map<KeyChord, ShortcutAction, ...>`, which
|
||||
uses the `KeyChord` to lookup the `ShortcutAction`. We'll need to introduce a
|
||||
new type `ActionAndArgs`:
|
||||
|
||||
```csharp
|
||||
runtimeclass ActionAndArgs
|
||||
{
|
||||
ShortcutAction Action;
|
||||
IActionArgs Args;
|
||||
}
|
||||
```
|
||||
|
||||
and we'll change the map in `AppKeyBindings` to a `std::unordered_map<KeyChord,
|
||||
ActionAndArgs, ...>`.
|
||||
|
||||
When we're parsing keybindings, we'll need to construct args for each of the
|
||||
events to go with each binding. When we find some key chord bound to a given
|
||||
Action, we'll construct the `IActionArgs` for that action. For many actions,
|
||||
these args will be an empty class. However, when we do find an action that needs
|
||||
additional parsing, `AppKeyBindingsSerialization` will do the extra work to
|
||||
parse the args for that action.
|
||||
|
||||
We'll keep a collection of functions that can be used for quickly determining
|
||||
how to parse the args for an action if necessary. This map will be a
|
||||
`std::unordered_map<ShortcutAction, function<IActionArgs(Json::Value)>>`. For
|
||||
most actions which don't require args, the function in this map will be set to
|
||||
nullptr, and we'll know that the action doesn't need to parse any more args.
|
||||
However, for actions that _do_ require args, we'll set up a global function that
|
||||
can be used to parse a json blob into an `IActionArgs`.
|
||||
|
||||
Once the `IActionArgs` is built for the keybinding, we'll set it in
|
||||
`AppKeyBindings` with a updated `AppKeyBindings::SetKeyBinding` call.
|
||||
`SetKeyBinding`'s signature will be updated to take a `ActionAndArgs` instead.
|
||||
Should an action not need arguments, the `Args` member can be left `null` in the
|
||||
`ActionAndArgs`.
|
||||
|
||||
### Executing KeyBinding Actions with Arguments
|
||||
|
||||
When we're handling a keybinding in `AppKeyBindings::_DoAction`, we'll trigger
|
||||
the event handlers with the `IActionArgs` we've stored in the map with the
|
||||
`ShortcutAction`.
|
||||
|
||||
Then, in `App`, we'll handle each of these events. We set up lambdas as event
|
||||
handlers for each event in `App::_HookupKeyBindings`. In each of those
|
||||
functions, we'll inspect the `IActionArgs` parameter, and use args from its
|
||||
implementation to call callbacks in the `App` class. We will update `App` to
|
||||
have methods defined with the actual keybinding function signatures.
|
||||
|
||||
Instead of:
|
||||
|
||||
```c++
|
||||
void App::_HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept
|
||||
{
|
||||
// ...
|
||||
bindings.NewTabWithProfile([this](const auto index) { _OpenNewTab({ index }); });
|
||||
}
|
||||
```
|
||||
|
||||
The code will look like:
|
||||
|
||||
```c++
|
||||
void App::_HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept
|
||||
{
|
||||
// ...
|
||||
bindings.NewTabWithProfile({ this, &App::_OpenNewTab });
|
||||
}
|
||||
// ...
|
||||
void App::_OpenNewTab(const TerminalApp::AppKeyBindings& sender, const NewTabEventArgs& args)
|
||||
{
|
||||
auto profileIndex = args.ProfileIndex();
|
||||
args.Handled(true);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Handling Keybinding Events
|
||||
|
||||
Common to all implementations of `IActionArgs` is the `Handled` property. This
|
||||
will let the app indicate if it was able to actually process a keybinding event
|
||||
or not. While in the large majority of cases, the events will all be marked
|
||||
handled, there are some scenarios where the Terminal will need to know if the
|
||||
event could not be performed. For example, in the case of the `copy` event, the
|
||||
Terminal is only capable of copying text if there's actually a selection active.
|
||||
If there isn't a selection active, the `App` should make sure to not mark the
|
||||
event as not handled (it will leave `args.Handled(false)`). The App should only
|
||||
mark an event handled if it has actually dispatched the event.
|
||||
|
||||
When an event is handled, we'll make sure to return `true` from
|
||||
`AppKeyBindings::TryKeyChord`, so that the terminal does not actually process
|
||||
that keypress. For events that were not handled by the application, the terminal
|
||||
will get another chance to dispatch the keypress.
|
||||
|
||||
### Serializing KeyBinding Arguments
|
||||
|
||||
Similar to how we parse arguments from the json, we'll need to update the
|
||||
`AppKeyBindingsSerialization` code to be able to serialize the arguments from a
|
||||
particular `IActionArgs`.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
### Keybindings in the New Tab Dropdown
|
||||
|
||||
Small modifications will need to be made to the code responsible for the new tab
|
||||
dropdown. The new tab dropdown currently also displays the keybindings for each
|
||||
profile in the new tab dropdown. It does this by querying for the keybinding
|
||||
associated with each action. As we'll be removing the old `ShortcutAction`s that
|
||||
this dropdown uses, we'll need a new way to find which key chord corresponds to
|
||||
opening a given profile.
|
||||
|
||||
We'll need to be able to not only lookup a keybinding by `ShortcutAction`, but
|
||||
also by a `ShortcutAction` and `IActionArgs`. We'll need to update the
|
||||
`AppKeyBindings::GetKeyBinding` method to also accept a `IActionArgs`. We'll
|
||||
also probably want each `IActionArgs` implementation to define an
|
||||
`Equals(IActionArgs)` method, so that we can easily check if two different
|
||||
`IActionArgs` are the same in this method.
|
||||
|
||||
## Capabilities
|
||||
### Accessibility
|
||||
|
||||
N/A
|
||||
|
||||
### 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 keybindings are ignored. Currently, we
|
||||
already gracefully ignore keybindings that have invalid `keys` or invalid
|
||||
`commands`. We'll need to add additional validation on invalid sets of `args`.
|
||||
When we're parsing the args from a Json blob, we'll make sure to only ever look
|
||||
for keys we're expecting and ignore everything else.
|
||||
|
||||
If a keybinding requires certain args, but those args are not provided, we'll
|
||||
need to make sure those args each have reasonable default values to use. If for
|
||||
any reason a reasonable default can't be used for a keybinding argument, then
|
||||
we'll need to make sure to display an error dialog to the user for that
|
||||
scenario.
|
||||
|
||||
When we're re-serializing settings, we'll only know about the keybinding arg
|
||||
keys that were successfully parsed. Other keys will be lost on re-serialization.
|
||||
|
||||
### Compatibility
|
||||
|
||||
This change will need to carefully be crafted to enable upgrading the legacy
|
||||
keybindings seamlessly. For most actions, the upgrade should be seamless. Since
|
||||
they already don't have args, their serializations will remain exactly the same.
|
||||
|
||||
However, for the following actions that we'll be removing in favor of actions
|
||||
with arguments, we'll need to leave legacy deserialization in place to be able
|
||||
to find these old actions, and automatically build the correct `IActionArgs`
|
||||
for them:
|
||||
|
||||
* `newTabProfile<n>`
|
||||
- We'll need to make sure to build args with the right `profileIndex`
|
||||
corresponding to the old action.
|
||||
* `switchToTab<n>`
|
||||
- We'll need to make sure to build args with the right `index` corresponding
|
||||
to the old action.
|
||||
* `resizePane<direction>` and `moveFocus<direction>`
|
||||
- We'll need to make sure to build args with the right `direction`
|
||||
corresponding to the old action.
|
||||
* `scroll<direction>`
|
||||
- We'll need to make sure to build args with the right `amount` value
|
||||
corresponding to the old action. `Up` will be -1, and `Down` will be 1.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
N/A
|
||||
|
||||
## Potential Issues
|
||||
|
||||
N/A
|
||||
|
||||
## Future considerations
|
||||
|
||||
* Should we support some sort of conversion from num keys to an automatic arg?
|
||||
For example, by default, <kbd>Alt+<N></kbd> to focuses the
|
||||
Nth tab. Currently, those are 8 separate entries in the keybindings. Should we
|
||||
enable some way for them be combined into a single binding entry, where the
|
||||
binding automatically receives the number pressed as an arg? I couldn't find
|
||||
any prior art of this, so it doesn't seem worth it to try and invent
|
||||
currently. This might be something that we want to loop back on, but for the
|
||||
time being, it remains out of scope of this PR.
|
||||
* When we inevitable support extensions, we'll need to allow extensions to also
|
||||
be able to support their own custom keybindings and args. We'll probably want
|
||||
to pass the settings to the extension to have the extension parse its own
|
||||
settings. We'll want to be able to ask the extension for its own set of
|
||||
`ActionAndArgs`<sup>[1]</sup> that it builds from the `keybindings`. Once we
|
||||
have that set of actions, we'll be able to store them locally, and dispatch
|
||||
them quickly.
|
||||
- [1] We probably won't be able to use the `ActionAndArgs` class directly,
|
||||
since that class is specific to the actions we define. We'll need another
|
||||
way for extensions to be able to uniquely identify their own actions.
|
||||
|
||||
## Resources
|
||||
|
||||
N/A
|
||||
|
||||
[#2285]: https://github.com/microsoft/terminal/issues/2285
|
||||
@@ -53,7 +53,7 @@ This feature will not impact reliability of Windows Terminal.
|
||||
|
||||
### Compatibility
|
||||
|
||||
With the implementation being mostly decoupled from the Windows Terminal app itself, no existing code/behaviors should break due to this feature.
|
||||
With the implementation being mostly decoupled from the Windows Terminal app itself, no existing code/behaviours should break due to this feature.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
|
||||
@@ -1,788 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,346 +0,0 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2019-11-13
|
||||
last updated: 2019-12-05
|
||||
issue id: #2325
|
||||
---
|
||||
|
||||
# Default Profile Settings
|
||||
|
||||
## Abstract
|
||||
|
||||
Oftentimes, users have some common settings that they'd like applied to all of
|
||||
their profiles, without needing to manually edit the settings of each of them.
|
||||
This doc will cover some of the many proposals on how to expose that
|
||||
functionality to the user in our JSON settings model. In this first document,
|
||||
we'll examine a number of proposed solutions, as well as state our finalized
|
||||
design.
|
||||
|
||||
## Inspiration
|
||||
|
||||
During the course of the pull request review on [#3369], the original pull
|
||||
request for this feature's implementation, it became apparent that the entire
|
||||
team has differing opinions on how this feature should be exposed to the user.
|
||||
This doc is born from that discussion.
|
||||
|
||||
## Solution Proposals
|
||||
|
||||
The following are a number of different proposals of different ways to achieve
|
||||
the proposed functionality:
|
||||
|
||||
1. [`defaultSettings` Profile object in the global settings](#proposal-1-defaultsettings-profile-object-in-the-global-settings)
|
||||
2. [`__default__` Profile object in the user's profiles](#proposal-2-__default__-profile-object-in-the-users-profiles)
|
||||
3. [Change `profiles` to an object with a `list` of profiles and a `defaults`](#proposal-3-change-profiles-to-an-object-with-a-list-of-profiles-and-a-defaults-object)
|
||||
object
|
||||
4. [`inheritFrom` in profiles](#proposal-4-inheritfrom-in-profiles)
|
||||
|
||||
### Proposal 1: `defaultSettings` Profile object in the global settings
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://aka.ms/terminal-profiles-schema",
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"defaultSettings":
|
||||
{
|
||||
"useAcrylic": true,
|
||||
"acrylicOpacity": 0.1,
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 10
|
||||
},
|
||||
"requestedTheme" : "dark",
|
||||
"showTabsInTitlebar" : true,
|
||||
"profiles":
|
||||
[
|
||||
{
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"name": "Windows PowerShell",
|
||||
"commandline": "powershell.exe",
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"name": "cmd",
|
||||
"commandline": "cmd.exe",
|
||||
"hidden": false
|
||||
}
|
||||
],
|
||||
"schemes": [],
|
||||
"keybindings": []
|
||||
}
|
||||
```
|
||||
#### Benefits
|
||||
|
||||
##### Clearly encapsulates the default profile settings
|
||||
Puts all the default profiles settings in one object. It's immediately obvious
|
||||
when scanning the file where the defaults are.
|
||||
|
||||
##### Simple to understand
|
||||
There's one object that applies to all the subsequent profiles, and that
|
||||
object is the `defaultSettings` object.
|
||||
|
||||
#### Concerns
|
||||
|
||||
##### What do we name this setting?
|
||||
People were concerned about the naming of this property. No one has a name that
|
||||
we're quite happy with:
|
||||
|
||||
* `defaultSettings`: This kinda seems to conflict conceptually with
|
||||
"defaults.json". It's different, but is that obvious?
|
||||
* `defaultProfileSettings`: Implies "settings of the default profile"
|
||||
* `defaults`: This kinda seems to conflict conceptually with "defaults.json"
|
||||
* `baseProfileSettings`: not the worst, but not terribly intuitive
|
||||
* Others considered with less enthusiasm
|
||||
- `profiles.defaults`: people don't love the idea of a `.`, but hey, VsCode does it.
|
||||
- `inheritedSettings`
|
||||
- `rootSettings`
|
||||
- `globalSettings`: again maybe conflicts a bit with other concepts/properties
|
||||
- `profileSettings`
|
||||
- `profilePrototype`
|
||||
|
||||
##### Why is there this random floating profile in the global settings?
|
||||
|
||||
Users may be confused about the purpose of this random `Profile` that's in the
|
||||
globals. What's that profile doing there? Is _it_ the default profile?
|
||||
|
||||
### Proposal 2: `__default__` Profile object in the user's profiles
|
||||
```json
|
||||
{
|
||||
"$schema": "https://aka.ms/terminal-profiles-schema",
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"requestedTheme" : "dark",
|
||||
"showTabsInTitlebar" : true,
|
||||
"profiles":
|
||||
[
|
||||
{
|
||||
"guid": "__default__",
|
||||
"useAcrylic": true,
|
||||
"acrylicOpacity": 0.1,
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 10
|
||||
},
|
||||
{
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"name": "Windows PowerShell",
|
||||
"commandline": "powershell.exe",
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"name": "cmd",
|
||||
"commandline": "cmd.exe",
|
||||
"hidden": false
|
||||
}
|
||||
],
|
||||
"schemes": [],
|
||||
"keybindings": []
|
||||
}
|
||||
```
|
||||
|
||||
#### Benefits
|
||||
##### Encapsulates the default profile settings
|
||||
Puts all the default profiles settings in one object. Probably not as clear as
|
||||
proposal 1, since it could be _anywhere_ in the list of profiles.
|
||||
|
||||
##### Groups default profile settings with profiles
|
||||
In this proposal, the default profile is grouped into the same list of objects
|
||||
as the other profiles. All the profiles, and the defaults are all under the
|
||||
`"profiles"` object. Makes sense.
|
||||
|
||||
#### Concerns
|
||||
##### Mysterious `__defaults__` GUID
|
||||
The only way to _definitively_ identify that this profile is special is by
|
||||
giving it a constant string. This string is _not_ a guid, which again, would be
|
||||
obvious.
|
||||
|
||||
##### Unintuitive
|
||||
Adding a profile that has a mysterious `guid` value use that profile as the
|
||||
"defaults" is _very_ unintuitive. Nothing aside from documentation would
|
||||
indicate to the user "hey, add this magic profile blob to use as defaults across
|
||||
all your profiles".
|
||||
|
||||
##### Why does this one profile object apply to all the others
|
||||
It might be unintuitive that one profile from the list of profiles affects all
|
||||
the others.
|
||||
|
||||
### Proposal 3: Change `profiles` to an object with a `list` of profiles and a `defaults` object
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://aka.ms/terminal-profiles-schema",
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"requestedTheme" : "dark",
|
||||
"showTabsInTitlebar" : true,
|
||||
"profiles":
|
||||
{
|
||||
"defaults": {
|
||||
"useAcrylic": true,
|
||||
"acrylicOpacity": 0.1,
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 10
|
||||
},
|
||||
"list":[
|
||||
{
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"name": "Windows PowerShell",
|
||||
"commandline": "powershell.exe",
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"name": "cmd",
|
||||
"commandline": "cmd.exe",
|
||||
"hidden": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"schemes": [],
|
||||
"keybindings": []
|
||||
}
|
||||
```
|
||||
|
||||
#### Benefits
|
||||
##### Groups default profile settings with profiles
|
||||
In this proposal, the default profile is grouped into the same object as the
|
||||
list of profiles. All the profiles, and the defaults are all under the
|
||||
`"profiles"` object. Makes sense.
|
||||
|
||||
##### Backwards compatible
|
||||
Fortunately, we can add this functionality _without breaking the existing
|
||||
schema_. With Jsoncpp, we can determine at runtime if an object is an _array_ or
|
||||
an _object_. If it's an array, we can fall back to the current behavior, safe in
|
||||
our knowledge that there's no defaults object. If the object is an array
|
||||
however, we can then dig into the object to find the default profile and the
|
||||
list of profiles.
|
||||
|
||||
#### Concerns
|
||||
##### Substantial schema change
|
||||
This is a pretty big delta to the settings schema. Instead of using `profiles`
|
||||
as a list of `Profile` objects, it instead becomes an object, with a list inside
|
||||
it.
|
||||
|
||||
As noted above, we could gracefully upgrade this. If the `profiles` object is a
|
||||
list, then we can assume there's no `defaults`. This ensures that user's current
|
||||
settings files don't break. This is not a major problem.
|
||||
|
||||
##### Adds another level of indentation to all profiles
|
||||
Some people just hate having things indented this much. 4 layers of indentation
|
||||
is quite a lot.
|
||||
|
||||
### Proposal 4: `inheritFrom` in profiles
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://aka.ms/terminal-profiles-schema",
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"requestedTheme" : "dark",
|
||||
"showTabsInTitlebar" : true,
|
||||
"profiles":
|
||||
[
|
||||
{
|
||||
"guid": "{11111111-1111-1111-1111-111111111111}",
|
||||
"hidden": true,
|
||||
"useAcrylic": true,
|
||||
"acrylicOpacity": 0.1,
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 10
|
||||
},
|
||||
{
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"inheritFrom": "{11111111-1111-1111-1111-111111111111}",
|
||||
"name": "Windows PowerShell",
|
||||
"commandline": "powershell.exe",
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"inheritFrom": "{11111111-1111-1111-1111-111111111111}",
|
||||
"name": "cmd",
|
||||
"commandline": "cmd.exe",
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"guid": "{0caa0dad-ffff-5f56-a8ff-afceeeaa6101}",
|
||||
"inheritFrom": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"name": "This is another CMD",
|
||||
"commandline": "cmd.exe /c myCoolScript.bat",
|
||||
"hidden": false
|
||||
}
|
||||
],
|
||||
"schemes": [],
|
||||
"keybindings": []
|
||||
}
|
||||
```
|
||||
|
||||
#### Benefits
|
||||
|
||||
##### Matches the existing settings model without major refactoring
|
||||
Simply adding a new property to `Profile` would not majorly alter the structure
|
||||
of the file.
|
||||
|
||||
##### Property name is unique
|
||||
`inheritFrom` is very unique relative to other keys we already have.
|
||||
|
||||
##### Powerful
|
||||
This lets the user have potentially many layers of settings grouping. These
|
||||
layers would let the user separate out common settings however they like,
|
||||
without forcing them to a single "default" profile. They could potentially have
|
||||
many "default" profiles, e.g.
|
||||
* one that's used for all their WSL profiles, with `startingDirectory` set to
|
||||
`~` and `fontFace` set to "Ubuntu Mono"
|
||||
* One that's used for all their powershell profiles
|
||||
|
||||
etc.
|
||||
|
||||
#### Concerns
|
||||
|
||||
##### GUIDs are not human friendly
|
||||
|
||||
Using the guid in the `inheritFrom` field is the only way to be sure we're
|
||||
uniquely identifying profiles. However, guids are notoriously un-friendly. The
|
||||
above example manually uses `"{11111111-1111-1111-1111-111111111111}"` as the
|
||||
guid of the "default" profile, but inheriting from other profiles with "real"
|
||||
GUIDs would be less understandable. Consider the "This is another CMD" case,
|
||||
where it's inheriting from the "cmd" profile. That `"inheritFrom"` value does
|
||||
not mean at a quick glance "cmd".
|
||||
|
||||
##### We have to make sure that there are no cycles as we're layering
|
||||
|
||||
This is mostly a technical challenge, but this does make the implementation a
|
||||
bit trickier.
|
||||
|
||||
##### How does this work with the settings UI?
|
||||
|
||||
When the user edits settings for a profile with the UI, do we only place the
|
||||
changes in the top-most profile?
|
||||
|
||||
How do we communicate in the UI that a profile is inheriting settings from other
|
||||
profiles?
|
||||
|
||||
##### Harder to mentally parse
|
||||
Maybe not as easy to mentally picture how one profile inherits from another. The
|
||||
user would probably need to manually build the tree of profile inheritance in
|
||||
their own head to understand how a profile gets its settings.
|
||||
|
||||
## Conclusions
|
||||
|
||||
After discussion the available options, the team has settled on proposal 3. The
|
||||
major selling points being:
|
||||
* It groups the new "default profile settings" with the rest of the profile
|
||||
settings
|
||||
* While being a schema change, it's not a _breaking_ schema change.
|
||||
* When looking at the settings, it's easy to understand how they're related
|
||||
|
||||
We also like the idea of proposal 4, but felt that it was too heavy-handed of an
|
||||
approach for this relatively simple feature. It's been added to the backlog of
|
||||
terminal features, tracked in [#3818].
|
||||
|
||||
## Resources
|
||||
|
||||
* Default Profile for Common Profile Settings (the original issue) [#2325]
|
||||
* Add support for "User Default" settings (the original PR) [#3369]
|
||||
* Add support for inheriting and overriding another profile's settings [#3818]
|
||||
|
||||
<!-- Footnotes -->
|
||||
[#2325]: https://github.com/microsoft/terminal/issues/2325
|
||||
[#3369]: https://github.com/microsoft/terminal/pull/3369
|
||||
[#3818]: https://github.com/microsoft/terminal/issues/3818
|
||||
@@ -1,135 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,107 +0,0 @@
|
||||
---
|
||||
author: Dustin Howett @DHowett-MSFT
|
||||
created on: 2019-07-19
|
||||
last updated: 2019-11-05
|
||||
issue id: "#2563"
|
||||
---
|
||||
|
||||
# Improvements to CloseOnExit
|
||||
|
||||
## Abstract
|
||||
|
||||
This specification describes an improvement to the `closeOnExit` profile feature and the `ITerminalConnection` interface that will offer greater flexibility and allow us to provide saner defaults in the face of unreliable software.
|
||||
|
||||
### Conventions and Terminology
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
|
||||
|
||||
## Inspiration
|
||||
|
||||
Other terminal emulators like ConEmu have a similar feature.
|
||||
|
||||
## Solution Design
|
||||
|
||||
### `ITerminalConnection` Changes
|
||||
|
||||
* The `TerminalConnection` interface will be augmented with an enumerator and a set of events regarding connection state transitions.
|
||||
* enum `TerminalConnection::ConnectionState`
|
||||
* This enum attempts to encompass all potential connection states, even those which do not make sense for a local terminal.
|
||||
* The wide variety of values will be useful to indicate state changes in a user interface.
|
||||
* `NotConnected`: All new connections will start out in this state
|
||||
* `Connecting`: The connection has been initiated, but has not yet completed connecting.
|
||||
* `Connected`: The connection is active.
|
||||
* `Closing`: The connection is being closed (usually by request).
|
||||
* `Closed`: The connection has been closed, either by request or from the remote end terminating successfully.
|
||||
* `Failed`: The connection was unexpectedly terminated.
|
||||
* event `StateChanged(ITerminalConnection, IInspectable)`
|
||||
* (the `IInspectable` argument is recommended and required for a typed event handler, but it will bear no payload.)
|
||||
* event `TerminalDisconnected` will be removed, as it is replaced by `StateChanged`
|
||||
* **NOTE**: A conforming implementation MUST treat states as a directed acyclic graph. States MUST NOT be transitioned in reverse.
|
||||
* A helper class may be provided for managing state transitions.
|
||||
|
||||
### `TerminalControl` Changes
|
||||
|
||||
* As the decision as to whether to close a terminal control hosting a connection that has transitioned into a terminal state will be made by the application, the unexpressive `Close` event will be removed and replaced with a `ConnectionStateChanged` event.
|
||||
* `event ConnectionStateChanged(TerminalControl, IInspectable)` event will project its connection's `StateChanged` event.
|
||||
* TerminalControl's new `ConnectionState` will project its connection's `State`.
|
||||
* (this is indicated for an eventual data binding; see Future Considerations.)
|
||||
|
||||
### Application and Settings
|
||||
|
||||
1. The existing `closeOnExit` profile key will be replaced with an enumerated string key supporting the following values (behaviors):
|
||||
* `always` - a tab or pane hosting this profile will always be closed when the launched connection reaches a terminal state.
|
||||
* `graceful` - a tab or pane hosting this profile will be closed if and only if the launched connection reaches the `Closed` terminal state.
|
||||
* `never` - a tab or pane hosting this profile will not automatically close.
|
||||
* See the Compatibility section for information on the legacy settings transition.
|
||||
* **The new default value for `closeOnExit` will be `graceful`.**
|
||||
2. `Pane` will remain responsible for making the final determination as to whether it is closed based on the settings of the profile it is hosting.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
* The existing `ITerminalConnection` implementations will be augmented to print out interesting and useful status information when they transition into a `Closed` or `Failed` state.
|
||||
* Example (ConPTY connection)
|
||||
* The pseudoconsole cannot be opened, or the process fails to launch.<br>`[failed to spawn 'thing': 0x80070002]`, transition to `Failed`.
|
||||
* The process exited unexpectedly.<br>`[process exited with code 300]`, transition to `Failed`.
|
||||
* The process exited normally.<br>`[process exited with code 0]`, transition to `Closed`.
|
||||
* _The final message will always be printed_ regardless of user configuration.
|
||||
* If the user's settings specify `closeOnExit: never/false`, the terminal hosting the connection will never be automatically closed. The message will remain on-screen.
|
||||
* If the user's settings specify `closeOnExit: graceful/true`, the terminal hosting the connection _will_ automatically be closed if the connection's state is `Closed`. A connection in the `Failed` state will not be closed, and the message will remain on-screen.
|
||||
* If the user's settings specify `closeOnExit: always`, the terminal hosting the connection will be closed. The message will not be seen.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
This will give users of all technologies a way to know when their shell has failed to launch or has exited with an unexpected status code.
|
||||
|
||||
### Security
|
||||
|
||||
There will be no impact to security.
|
||||
|
||||
### Reliability
|
||||
|
||||
Windows Terminal will no longer immediately terminate on startup if the user's shell doesn't exist.
|
||||
|
||||
### Compatibility
|
||||
|
||||
There is an existing `closeOnExit` _boolean_ key that a user may have configured in profiles.json. The boolean values should map as follows:
|
||||
|
||||
* `true` -> `graceful`
|
||||
* `false` -> `never`
|
||||
|
||||
This will make for a clean transition to Windows Terminal's sane new defaults.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
## Potential Issues
|
||||
|
||||
There will be no impact to Performance, Power or Efficiency.
|
||||
|
||||
## Future considerations
|
||||
|
||||
* Eventually, we may want to implement a feature like "only close on graceful exit if the shell was running for more than X seconds". This puts us in a better position to do that, as we can detect graceful and clumsy exits more readily.
|
||||
* (potential suggestion: `{ "closeOnExit": "10s" }`
|
||||
* The enumerator values for transitioning connection states will be useful for connections that require internet access.
|
||||
* Since the connection states are exposed through `TerminalControl`, they should be able to be data-bound to other Xaml elements. This can be used to provide discrete UI states for terminal controls, panes or tabs _hosting_ terminal controls.
|
||||
* Example: a tab hosting a terminal control whose connection has been broken MAY display a red border.
|
||||
* Example: an inactive tab that reaches the `Connected` state MAY flash to indicate that it is ready.
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
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.
|
||||
|
Before Width: | Height: | Size: 60 KiB |
@@ -1,559 +0,0 @@
|
||||
---
|
||||
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
|
||||
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 67 KiB |
@@ -1,126 +0,0 @@
|
||||
---
|
||||
author: Kaiyu Wang KaiyuWang16
|
||||
created on: 2019-12-10
|
||||
last updated: 2019-12-10
|
||||
issue id: #605
|
||||
---
|
||||
|
||||
# Search in Terminal
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec is for feature request #605 "Search". It goes over the details of a new feature that allows users to search text in Terminal, within one tab or from all tabs. Expected behavior and design of this feature is included. Besides, future possible follow-up works are also addressed.
|
||||
|
||||
## Inspiration
|
||||
|
||||
One of the superior features of iTerm2 is it's content search. The search comes in two variants: search from active tab and search from all tabs. In almost any editor, there is an roughly equivalent string search. We also want to realize search experience in Terminal. There will be two variants, search within one tab or from multiple tabs. We will start with one-tab search implementation.
|
||||
|
||||
## Solution Design
|
||||
|
||||
Our ultimate goal is to provide both search within one tab and search from all tabs experiences. But we can start with one-tab search. The search experience should have following features:
|
||||
|
||||
1. The search is triggered by KeyBindings. A new setting property named "find" will be enabled in the Json file. The user can set their own key bindings for search. The default is <kbd>ctrl+shift+f</kbd>.
|
||||
2. The user search in a XAML TextBox, which is contained in a custom `SearchBoxControl`. The default position of the search box is the top right corner.
|
||||
3. We can have multiple search methods. The simplest one is exact text match. Other match methods include case-sensitive exact match and regex match. In the first phase, we will focus on case sensitive/insensitive text exact match.
|
||||
4. If currently there is no active selection, the search starts from the last line of the mutableViewport. If there is an active selection, we start from the previous or the next text of the selected text. We automatically go around if we reach the start point of the search.
|
||||
5. The user should be able to fully interact with the terminal when the search box is on screen.
|
||||
6. For accessibility concerns, the user should be able to navigate all the interactive elements on the search box using keyboard tab if the search box is focused. Searchbox could be created and closed with keyboard bindings. Close is usually bound to Esc.
|
||||
|
||||
Conhost already has a module for search. It implements case sensitive or insensitive exact text match search, and it provides methods to select the found word. However, we want to make search as a shared component between Terminal and Console host. Now search module is part of Conhost, and its dependencies include BufferOut and some other types in ConHost such as SCREEN_INFORMATION. In order to make Search a shared component, we need to remove its dependency on ConHost types. BufferOut is already a shared component, but we need to make sure there is no other Conhost dependency.
|
||||
|
||||
We will create a `SearchBoxControl` Xaml `UserControl` element. When a search process begins, a `SearchBoxControl` object will be created and attached to `TermControl` root grid. In other words, one SearchBox is added for each `TermControl`. The reasons for this design is:
|
||||
|
||||
1. Each `TermControl` object is a Terminal Window and has a individual text buffer. In phase 1 we are going to search within the current terminal text buffer.
|
||||
2. If we put the search box under TerminalApp, then the search can only happen on the current focused Terminal.
|
||||
3. If the community does not like the current design, we can lift SearchBox to a higher level.
|
||||
|
||||
### Search process implementation
|
||||
1. Once the user press <kbd>ctrl+shift+f</kbd> (or user's custom key binding), a new `SearchBoxControl` object will be created and attached as a child of `TermControl`. Focus will move to the TextBox within the `SearchBoxControl`.
|
||||
2. Search is performed on a XAML TextBox. Once the user presses Enter or click up/down arrow button, we start to search from the last line of the current viewport or the current selection, and try to find the exact text in the text buffer. The nearest searched one will be selected. Then the search start point will be set to the selected text. The next search will start before or after the previous searched text.
|
||||
3. We re-use the Search module in conhost. It performs the search in a brute-force approach. Starting from every position in the text buffer, the search algorithm compares the span of the searched string with buffer characters, and if the current buffer text matches the whole string, it will return store the position of the text in the buffer and return. The stored position information will be used for selection.
|
||||
3. The user can choose to search up or down. Search module realizes this, we just need to set a boolean flag. Default is search up.
|
||||
4. The user can choose to do case sensitive or insensitive match. This also realized by Search module by setting a boolean flag. Default is search case-insensitively.
|
||||
5. Tab navigation is realized by XAML. We just need to set TabNavigation="Cycle" in `SearchBoxControl`.
|
||||
6. If the user clicks on the "X" button or press <kbd>Esc</kbd>, the search box will disappear and the object will be destructed and detached from the `TermControl` XAML tree. In phase one we do not store any state.
|
||||
7. We need to guarantee full interaction with the terminal when the search box is open. To achieve this, search box and terminal input should be separated. If the current keyboard focus is on the search box, then keydown events will be handled on "search box level".
|
||||
|
||||
## UI/UX Design
|
||||
|
||||

|
||||
|
||||
Above is the `SearchBoxControl` in dark theme and light theme.
|
||||
- The two buttons with up/down arrows controls the search direction, Each button will be styled to indicate which search direction is currently selected.
|
||||
- The button with a "Aa" icon, if pressed, means that we are searching case-sensitivity.
|
||||
- The current style puts all elements - the `X` button, the text box and the search pattern control buttons on one single line. This ensures that the `SearchBoxControl` won't be too high and block terminal text. This is similar with VSCode. Another possible layout style is to put elements in multiple layers. This will occupy more lines, but the search dialog will narrower. Considering that there is not many elements, we do not need multiple layers.
|
||||
|
||||

|
||||
|
||||
The search box defaults to be on the top right corner of the Terminal window. If the current tab is split into panes, each pane will have a individual searchbox.
|
||||
|
||||
#### Search process
|
||||
1. The user presses <kbd>ctrl+shift+f</kbd> (or user's custom key binding) to open the search box. Focus will move to the TextBox.
|
||||
2. Search is performed on a XAML TextBox. Once the user presses Enter or click up/down arrow button, the search starts and searched text will be selected. Next search will be performed beginning from the current selection and go towards up/down.
|
||||
3. The user can choose to search up or down by selecting up arrow or down arrow buttons. The chosen button will be styled to indicate it is selected. If the user does not click the arrows buttons, the default direction is up.
|
||||
4. The user can choose to do case sensitive or insensitive match by checking a check box. The default is case insensitive.
|
||||
5. If the search box is focused, the user can navigate all the elements on the search box using tab. When selected, press Enter equals to click.
|
||||
6. If the user click the "X" button or press <kbd>Esc</kbd>, the search stopped and the search box disappears and focus will move back to Terminal.
|
||||
7. Once the search box is closed (exiting search mode), the selection will still be there. This coincides with the current VS Code and cmd experience. To get rid of the selection, the user can just click other area of the window.
|
||||
8. If the user clicks on the terminal when the search box is open, it will draw focus back to the terminal from the search box. The search box will still stay open.
|
||||
9. The user can interact with the terminal when the search box is open, which means that the user can scroll the terminal content, or input text when the focus is on the terminal control.
|
||||
10. If the user switches tabs while the search box is open, the focus will be moved back to the terminal.
|
||||
|
||||
## Capabilities
|
||||
|
||||
1. The user can search exact matched text in the text buffer of the Terminal Screen.
|
||||
2. The user can choose to search case sensitively and insensitively.
|
||||
3. The user can search up or down.
|
||||
4. Found text will be selected.
|
||||
5. The search will start from the active selected text (inclusive) if there is one, or the end of the written text.
|
||||
6. The search will automatically go around when it reaches the starting point.
|
||||
7. The user can use Tab to navigate all the elements in the search box.
|
||||
8. The user can search in the opposite direction with <kbd>Shift + Enter</kbd>
|
||||
|
||||
### Accessibility
|
||||
|
||||
The user should be able to use search by keyboard only.
|
||||
Once the searchbox is focused, the user can navigate between elements in the search box using Tab. And "click" using Enter.
|
||||
|
||||
### Security
|
||||
|
||||
This feature should not introduce any new security issues.
|
||||
|
||||
### Reliability
|
||||
|
||||
1. The key input of Terminal command line and the search box should be separated. Search box should not block interaction with the command line when it is open.
|
||||
2. The search box should not block too much text. The search box only occupies one line, so it won't have big impact on the readability of the terminal output.
|
||||
|
||||
### Compatibility
|
||||
|
||||
This feature won't break existing features of Terminal.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
This feature only launches in need. It does not impact the performance of Terminal.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
1. If the terminal window is not wide enough for the search box to be visible, the buttons on the right of the `TextBox` will become invisible, but the `TextBox` is still visible and the window could not be narrower than the `TextBox`. This is similar to the behavior of other editors. Please see the image below:
|
||||

|
||||
2. If the terminal window is not high enough for the search box to be visible, the whole terminal screen, including the `SearchBoxControl` can disappear. This is similar to the behavior of other editors.
|
||||
|
||||
## Future considerations
|
||||
|
||||
In version 1, we want realize a case sensitive/insensitive exact text match. But we may consider the following features in version 2:
|
||||
|
||||
1. Add "Find" button in dropdown menu to trigger search. This enables the search feature to be operated with mouse only. However, this is not required by Accessibility so we do not cover this in phase one.
|
||||
2. Search from all tabs. For Version 1 we just want to realize search within one tab. However, the community also requests search from all tabs. This may require a big change to the search algorithm, but it is not seen as a popular use scenario, so we put it future phase. To implement multi-tab search, we can let TerminalPage or App own a `SearchBoxControl` object, and provide the text buffer of the current focused terminal. We need to change the search algorithm.
|
||||
3. Regular expression match. This is a useful search pattern and is implemented in some editors. However, this use scenario is not used as much as exact text search, thus, we put it in future phase.
|
||||
4. Search history. Sometimes users would do the same search for several times, thus, storing the search history is useful. This is not realized by VSCode so it would be a good highlighting point in the future.
|
||||
5. High-light while you type. Emphasizing all the other matches in the buffer with an outline or selection with another color. This provides a clearer view of searched text. But we need to change the search and selection algorithm, so we put it in the future phase.
|
||||
6. Add size handle. Some text editors let the user resize the search box, and there is a size handle on the left side of the search box. This helps user when they search for long text. If the community desires it we may add a similar feature.
|
||||
|
||||
This open issue tracks the phase features of Search: https://github.com/microsoft/terminal/issues/3920
|
||||
|
||||
## Resources
|
||||
|
||||
Github Issue: https://github.com/microsoft/terminal/issues/605
|
||||
@@ -1,739 +0,0 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2019-11-08
|
||||
last updated: 2020-01-15
|
||||
issue id: #607
|
||||
---
|
||||
|
||||
# Commandline Arguments for the Windows Terminal
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec outlines the changes necessary for Windows Terminal to support
|
||||
commandline arguments. These arguments can be used to enable customized launch
|
||||
scenarios for the Terminal, such as booting directly into a specific profile or
|
||||
directory.
|
||||
|
||||
## Inspiration
|
||||
|
||||
Since the addition of the "execution alias" `wt.exe` which enables launching the
|
||||
Windows Terminal from the commandline, we've always wanted to support arguments
|
||||
to enable custom launch scenarios. This need was amplified by requests like:
|
||||
* [#576], which wanted to add jumplist entries for the Windows Terminal, but was
|
||||
blocked because there was no way of communicating to the Terminal _which_
|
||||
profile it wanted to launch
|
||||
* [#1060] - being able to right-click in explorer to "open a Windows Terminal
|
||||
Here" is great, but would be more powerful if it could also provide options to
|
||||
open specific profiles in that directory.
|
||||
* [#2068] - We want the user to be able to (from inside the Terminal) not only
|
||||
open a new window with the default profile, but also open the new window with
|
||||
a specific profile.
|
||||
|
||||
Additionally, the final design for the arguments was heavily inspired by the
|
||||
arguments available to `tmux`, which also enables robust startup configuration
|
||||
through commandline arguments.
|
||||
|
||||
## User Stories
|
||||
|
||||
Lets consider some different ways that a user or developer might want want to
|
||||
use commandline arguments, to help guide the design.
|
||||
|
||||
1. A user wants to open the Windows Terminal with their default profile.
|
||||
- This one is easy, it's already provided with simply `wt`.
|
||||
2. A user wants to open the Windows Terminal with a specific profile from their
|
||||
list of profiles.
|
||||
3. A user wants to open the Windows Terminal with their default profile, but
|
||||
running a different commandline than usual.
|
||||
4. A user wants to know the list of arguments supported by `wt.exe`.
|
||||
5. A user wants to see their list of profiles, so they can open one in
|
||||
particular
|
||||
6. A user wants to open their settings file, without needing to open the
|
||||
Terminal window.
|
||||
7. A user wants to know what version of the Windows Terminal they are running,
|
||||
without needing to open the Terminal window.
|
||||
8. A user wants to open the Windows Terminal at a specific location on the
|
||||
screen
|
||||
9. A user wants to open the Windows Terminal in a specific directory.
|
||||
10. A user wants to open the Windows Terminal with a specific size
|
||||
11. A user wants to open the Windows Terminal with only the default settings,
|
||||
ignoring their user settings.
|
||||
12. A user wants to open the Windows Terminal with multiple tabs open
|
||||
simultaneously, each with different profiles, starting directories, even
|
||||
commandlines
|
||||
13. A user wants to open the Windows Terminal with multiple tabs and panes open
|
||||
simultaneously, each with different profiles, starting directories, even
|
||||
commandlines, and specific split sizes
|
||||
14. A user wants to use a file to provide a reusable startup configuration with
|
||||
many steps, to avoid needing to type the commandline each time.
|
||||
|
||||
## Solution Design
|
||||
|
||||
### Proposal 1 - Parameters
|
||||
|
||||
Initially, I had considered arguments in the following style:
|
||||
|
||||
* `--help`: Display the help message
|
||||
* `--version`: Display version info for the Windows Terminal
|
||||
* `--list-profiles`: Display a list of the available profiles
|
||||
- `--all` to also show "hidden" profiles
|
||||
- `--verbose`? To also display GUIDs?
|
||||
* `--open-settings`: Open the settings file
|
||||
* `--profile <profile name>`: Start with the given profile, by name
|
||||
* `--guid <profile guid>`: Start with the given profile, by GUID
|
||||
* `--startingDirectory <path>`: Start in the given directory
|
||||
* `--initialRows <rows>`, `--initialCols <rows>`: Start with a specific size
|
||||
* `--initialPosition <x,y>`: Start at an initial location on the screen
|
||||
* `-- <commandline>`: Start with this commandline instead
|
||||
|
||||
However, this style of arguments makes it very challenging to start multiple
|
||||
tabs or panes simultaneously. How would a user start multiple panes, each with a
|
||||
different commandline? As configurations become more complex, these commandlines
|
||||
would quickly become hard to parse and understand for the user.
|
||||
|
||||
### Proposal 2 - Commands and Parameters
|
||||
|
||||
Instead, we'll try to separate these arguments by their responsibilities. Some
|
||||
of these arguments cause something to happen, like `help`, `version`, or
|
||||
`open-settings`. Other arguments act more like modifiers, like for example
|
||||
`--profile` or `--startingDirectory`, which provide additional information to
|
||||
the action of _opening a new tab_. Lets try and define these concepts more
|
||||
clearly.
|
||||
|
||||
**Commands** are arguments that cause something to happen. They're provided in
|
||||
`kebab-case`, and can have some number of optional or required "parameters".
|
||||
|
||||
**Parameters** are arguments that provide additional information to "commands".
|
||||
They can be provided in either a long form or a short form. In the long form,
|
||||
they're provided in `--camelCase`, with two hyphens preceding the argument
|
||||
name. In short form, they're provided as just a single character preceded by a
|
||||
hyphen, like so: `-c`.
|
||||
|
||||
Let's enumerate some possible example commandlines, with explanations, to
|
||||
demonstrate:
|
||||
|
||||
### Sample Commandlines
|
||||
|
||||
```sh
|
||||
|
||||
# Runs the user's "Windows Powershell" profile in a new tab (user story 2)
|
||||
wt new-tab --profile "Windows Powershell"
|
||||
wt --profile "Windows Powershell"
|
||||
wt -p "Windows Powershell"
|
||||
|
||||
# Runs the user's default profile in a new tab, running cmd.exe (user story 3)
|
||||
wt cmd.exe
|
||||
|
||||
# display the help text (user story 4)
|
||||
wt help
|
||||
wt --help
|
||||
wt -h
|
||||
wt -?
|
||||
wt /?
|
||||
|
||||
# output the list of profiles (user story 5)
|
||||
wt list-profiles
|
||||
|
||||
# open the settings file, without opening the Terminal window (user story 6)
|
||||
wt open-settings
|
||||
|
||||
# Display version info for the Windows Terminal (user story 7)
|
||||
wt version
|
||||
wt --version
|
||||
wt -v
|
||||
|
||||
# Start the default profile in directory "c:/Users/Foo/dev/MyProject" (user story 9)
|
||||
wt new-tab --startingDirectory "c:/Users/Foo/dev/MyProject"
|
||||
wt --startingDirectory "c:/Users/Foo/dev/MyProject"
|
||||
wt -d "c:/Users/Foo/dev/MyProject"
|
||||
# Windows-style paths work too
|
||||
wt -d "c:\Users\Foo\dev\MyProject"
|
||||
|
||||
# Runs the user's "Windows Powershell" profile in a new tab in directory
|
||||
# "c:/Users/Foo/dev/MyProject" (user story 2, 9)
|
||||
wt new-tab --profile "Windows Powershell" --startingDirectory "c:/Users/Foo/dev/MyProject"
|
||||
wt --profile "Windows Powershell" --startingDirectory "c:/Users/Foo/dev/MyProject"
|
||||
wt -p "Windows Powershell" -d "c:/Users/Foo/dev/MyProject"
|
||||
|
||||
# open a new tab with the "Windows Powershell" profile, and another with the
|
||||
# "cmd" profile (user story 12)
|
||||
wt new-tab --profile "Windows Powershell" ; new-tab --profile "cmd"
|
||||
wt --profile "Windows Powershell" ; new-tab --profile "cmd"
|
||||
wt --profile "Windows Powershell" ; --profile "cmd"
|
||||
wt --p "Windows Powershell" ; --p "cmd"
|
||||
|
||||
# run "my-commandline.exe with some args" in a new tab
|
||||
wt new-tab my-commandline.exe with some args
|
||||
wt my-commandline.exe with some args
|
||||
|
||||
# run "my-commandline.exe with some args and a ; literal semicolon" in a new
|
||||
# tab, and in another tab, run "another.exe running in a second tab"
|
||||
wt my-commandline.exe with some args and a \; literal semicolon ; new-tab another.exe running in a second tab
|
||||
|
||||
# Start cmd.exe, then split it vertically (with the first taking 70% of it's
|
||||
# space, and the new pane taking 30%), and run wsl.exe in that pane (user story 13)
|
||||
wt cmd.exe ; split-pane --target 0 -V -% 30 wsl.exe
|
||||
wt cmd.exe ; split-pane -% 30 wsl.exe
|
||||
|
||||
# Create a new window with the default profile, create a vertical split with the
|
||||
# default profile, then create a horizontal split in the second pane and run
|
||||
# "media.exe" (user story 13)
|
||||
wt new-tab ; split-pane -V ; split-pane --target 1 -H media.exe
|
||||
wt new-tab ; split-pane -V ; split-pane -t 1 -H media.exe
|
||||
|
||||
```
|
||||
|
||||
## `wt` Syntax
|
||||
|
||||
The `wt` commandline is divided into two main sections: "Options", and "Commands":
|
||||
|
||||
`wt [options] [command ; ]...`
|
||||
|
||||
Options are a list of flags and other parameters that can control the behavior
|
||||
of the `wt` commandline as a whole. Commands are a semicolon-delimited list of
|
||||
commands and arguments for those commands.
|
||||
|
||||
If no command is specified in a `command`, then the command is assumed to be a
|
||||
`new-tab` command by default. So, for example, `wt cmd.exe` is interpreted the
|
||||
same as `wt new-tab cmd.exe`.
|
||||
|
||||
To take this a step further, empty commands surrounded by semicolons will also
|
||||
be interpreted as `new-tab` commands with the default parameters, so `wt ; ; ;`
|
||||
can be used to open the windows terminal with **4** new tabs. Effectively, that
|
||||
commandline expands to `wt new-tab ; new-tab ; new-tab ; new-tab`.
|
||||
|
||||
<!--
|
||||
### Aside: What should the default command be?
|
||||
|
||||
These are notes from my draft intentionally left here to help understand the
|
||||
conclusion that new-tab should be the default command.
|
||||
|
||||
Should the default command be `new-window` or `new-tab`?
|
||||
|
||||
`new-window` makes sense to take params like `--initialPosition`,
|
||||
`--initialRows`/`--initialCols`, and _implies_ `new-tab`. However, chained
|
||||
commands that want to open in the same window _need_ to specify `new-tab`,
|
||||
otherwise they'll all appear in new windows.
|
||||
|
||||
If it's `new-tab`, then how do `--initialRows` (etc) work? `new-tab` generally
|
||||
_doesn't_ accept those parameters, because it's going to be inheriting the
|
||||
parent's window size. Do we just ignore them for subsequent invocations? I
|
||||
suppose that makes sense, once the first tab has set those, then the other tabs
|
||||
can't really change them.
|
||||
|
||||
When dealing with a file full of startup commands, we'll assume all of them are
|
||||
intended for the given window. So the first `new-tab` in the file will create
|
||||
the window, and all subsequent `new-tab` commands will create tabs in that same
|
||||
window.
|
||||
-->
|
||||
|
||||
### Options
|
||||
|
||||
#### `--help,-h,-?,/?,`
|
||||
Runs the `help` command.
|
||||
|
||||
#### `--version,-v`
|
||||
Runs the `version` command.
|
||||
|
||||
#### `--session,-s session-id`
|
||||
Run these commands in the given Windows Terminal session. Enables opening new
|
||||
tabs in already running Windows Terminal windows. This feature is dependent upon
|
||||
other planned work landing, so is only provided as an example, of what it might
|
||||
look like. See [Future Considerations](#Future-Considerations) for more details.
|
||||
|
||||
#### `--file,-f configuration-file`
|
||||
Run these commands in the given Windows Terminal session. Enables opening new
|
||||
tabs in already running Windows Terminal windows. See [Future
|
||||
Considerations](#Future-Considerations) for more details.
|
||||
|
||||
### Commands
|
||||
|
||||
#### `help`
|
||||
|
||||
`help`
|
||||
|
||||
Display the help message.
|
||||
|
||||
#### `version`
|
||||
|
||||
`version`
|
||||
|
||||
Display version info for the Windows Terminal.
|
||||
|
||||
#### `open-settings`
|
||||
|
||||
`open-settings [--defaults,-d]`
|
||||
|
||||
Open the settings file. If this command is provided alone, it does not open the
|
||||
terminal window.
|
||||
|
||||
**Parameters**:
|
||||
* `--defaults,-d`: Open the `defaults.json` file instead of the `profiles.json`
|
||||
file.
|
||||
|
||||
#### `list-profiles`
|
||||
|
||||
`list-profiles [--all,-A] [--showGuids,-g]`
|
||||
|
||||
Displays a list of each of the available profiles. Each profile displays it's
|
||||
name, separated by newlines.
|
||||
|
||||
**Parameters**:
|
||||
* `--all,-A`: Show all profiles, including profiles marked `"hidden": true`.
|
||||
* `--showGuids,-g`: In addition to showing names, also list each profile's
|
||||
guid. These GUIDs should probably be listed _first_ on each line, to make
|
||||
parsing output easier.
|
||||
|
||||
#### `new-tab`
|
||||
|
||||
`new-tab [--initialPosition x,y]|[--maximized]|[--fullscreen] [--initialRows rows] [--initialCols cols] [terminal_parameters]`
|
||||
|
||||
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.
|
||||
|
||||
**Parameters**:
|
||||
* `--initialPosition x,y`: Create the new Windows Terminal window at the given
|
||||
location on the screen in pixels. This parameter is only used when initially
|
||||
creating the window, and ignored for subsequent `new-tab` commands. When
|
||||
combined with any of `--maximized` or `--fullscreen`, an error message will be
|
||||
displayed to the user, indicating that an invalid combination of arguments was
|
||||
provided.
|
||||
* `--initialRows rows`: Create the terminal window with `rows` rows (in
|
||||
characters). If omitted, uses the value from the user's settings. This
|
||||
parameter is only used when initially creating the window, and ignored for
|
||||
subsequent `new-tab` commands. When combined with any of `--maximized` or
|
||||
`--fullscreen`, an error message will be displayed to the user, indicating
|
||||
that an invalid combination of arguments was provided.
|
||||
* `--initialCols cols`: Create the terminal window with `cols` cols (in
|
||||
characters). If omitted, uses the value from the user's settings. This
|
||||
parameter is only used when initially creating the window, and ignored for
|
||||
subsequent `new-tab` commands. When combined with any of `--maximized` or
|
||||
`--fullscreen`, an error message will be displayed to the user, indicating
|
||||
that an invalid combination of arguments was provided.
|
||||
* `[terminal_parameters]`: See [[terminal_parameters]](#terminal_parameters).
|
||||
|
||||
|
||||
#### `split-pane`
|
||||
|
||||
`split-pane [--target,-t target-pane] [-H]|[-V] [--percent,-% split-percentage] [terminal_parameters]`
|
||||
|
||||
Creates a new pane in the currently focused tab by splitting the given pane
|
||||
vertically or horizontally.
|
||||
|
||||
**Parameters**:
|
||||
* `--target,-t target-pane`: Creates a new split in the given `target-pane`.
|
||||
Each pane has a unique index (per-tab) which can be used to identify them.
|
||||
These indices are assigned in the order the panes were created. If omitted,
|
||||
defaults to the index of the currently focused pane.
|
||||
* `-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.
|
||||
* `--percent,-% split-percentage`: Designates the amount of space that the new
|
||||
pane should take as a percentage of the parent's space. If omitted, the pane
|
||||
will take 50% by default.
|
||||
* `[terminal_parameters]`: See [[terminal_parameters]](#terminal_parameters).
|
||||
|
||||
#### `focus-tab`
|
||||
|
||||
`focus-tab [--target,-t tab-index]`
|
||||
|
||||
Moves focus to a given tab.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
* `--target,-t tab-index`: moves focus to the tab at index `tab-index`. If omitted,
|
||||
defaults to `0` (the first tab).
|
||||
|
||||
#### `focus-pane`
|
||||
|
||||
`focus-pane [--target,-t target-pane]`
|
||||
|
||||
Moves focus within the currently focused tab to a given pane.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
* `--target,-t target-pane`: moves focus to the given `target-pane`. Each pane
|
||||
has a unique index (per-tab) which can be used to identify them. These
|
||||
indices are assigned in the order the panes were created. If omitted,
|
||||
defaults to the index of the currently focused pane (which is effectively a
|
||||
no-op).
|
||||
|
||||
#### `move-focus`
|
||||
|
||||
`move-focus [--direction,-d direction]`
|
||||
|
||||
Moves focus within the currently focused tab in the given direction.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
* `--direction,-d direction`: moves focus in the given `direction`. `direction`
|
||||
should be one of [`left`, `right`, `up`, `down`]. If omitted, does not move
|
||||
the focus at all (resulting in a no-op).
|
||||
|
||||
#### `[terminal_parameters]`
|
||||
|
||||
Some of the preceding commands are used to create a new terminal instance.
|
||||
These commands are listed above as accepting `[terminal_parameters]` as a
|
||||
parameter. For these commands, `[terminal_parameters]` can be any of the
|
||||
following:
|
||||
|
||||
`[--profile,-p profile-name] [--startingDirectory,-d starting-directory] [commandline]`
|
||||
|
||||
* `--profile,-p profile-name`: Use the given profile to open the new tab/pane,
|
||||
where `profile-name` is the `name` or `guid` of a profile. If `profile-name`
|
||||
does not match _any_ profiles, uses the default.
|
||||
* `--startingDirectory,-d starting-directory`: Overrides the value of
|
||||
`startingDirectory` of the specified profile, to start in `starting-directory`
|
||||
instead.
|
||||
* `commandline`: A commandline to replace the default commandline of the
|
||||
selected profile. If the user wants to use a `;` in this commandline, it
|
||||
should be escaped as `\;`.
|
||||
|
||||
Fundamentally, there's no reason that _all_ the current profile settings
|
||||
couldn't be overridden by commandline arguments. Practically, it might be
|
||||
unreasonable to create short form arguments for each and every Profile
|
||||
property, but the long form would certainly be reasonable.
|
||||
|
||||
The arguments listed above represent both special cases of the profile settings
|
||||
like `guid` and `name`, as well as high priority properties to add as arguments.
|
||||
* It doesn't really make sense to override `name` or `guid`, so those have been
|
||||
repurposed as arguments for selecting a profile.
|
||||
* `commandline` is a bit of a unique case - we're not explicitly using an
|
||||
argument to identify the start of the commandline here. This is to help avoid
|
||||
the need to parse and escape arguments to the client commandline.
|
||||
* `startingDirectory` is a _highly_ requested commandline argument, so that's
|
||||
been given priority in this spec.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
Following an investigation performed the week of Nov 18th, 2019, I've determined
|
||||
that we should be able to use the [CLI11] open-source library to parse
|
||||
our arguments. We'll need to add some additional logic on top of CLI11 in order
|
||||
to properly separate commands with `;`, but that's not impossible to achieve.
|
||||
|
||||
CLI11 will allow us to parse commandlines as a series of options, with a
|
||||
possible sub-command that takes its own set of parameters. This functionality
|
||||
will be used to enable our options & commands style of parameters.
|
||||
|
||||
When commands are parsed, each command will build an `ActionAndArgs` that can be
|
||||
used to tell the terminal what steps to perform on startup. The Terminal already
|
||||
uses these `ActionAndArgs` to perform actions like opening new tabs, panes,
|
||||
moving focus, etc.
|
||||
|
||||
In my initial investigation, it seemed as though the Terminal did not initialize
|
||||
the size of child controls initially. This meant that it wasn't possible to
|
||||
immediately create all the splits and tabs for the Terminal as passed on the
|
||||
commandline, because they'd open at a size of 0x0. To mitigate this, we'll
|
||||
handle dispatching these startup actions one at a time, waiting until the
|
||||
Terminal for an action is initialized or the command is otherwise completed
|
||||
before dispatching the next one.
|
||||
|
||||
This is a perhaps fragile way of handling the initialization. Ideally, there
|
||||
should be a way to dispatch all the commands _immediately_, before the Terminal
|
||||
fully initializes, so that the UI pops up in the state as specified in the
|
||||
commandline. This will be an area of active investigation as implementation is
|
||||
developed, to make the initialization of many commands as seamless as possible.
|
||||
|
||||
### Implementation plan
|
||||
|
||||
As this is a very complex feature, there will need to be a number of steps taken
|
||||
in the codebase to enable this functionality in a way that users are expecting.
|
||||
The following is a suggestion of the individual changelists that could be made
|
||||
to iteratively work towards fulling implementing this functionality.
|
||||
|
||||
* [x] Refactor `ShortcutAction` dispatching into its own class
|
||||
- Right now, the `AppKeyBindings` is responsible for triggering all
|
||||
`ActionAndArgs` events, but only based upon keystrokes while the Terminal is
|
||||
running. As we'll be re-using `ActionAndArgs` for handling startup events,
|
||||
we'll need a more generic way of dispatching those events.
|
||||
* [x] Add a `SplitPane` `ShortcutAction`, with a single parameter `split`,
|
||||
which accepts either `vertical`, `horizontal`, or `auto`.
|
||||
- Make sure to convert the legacy `SplitVertical` and `SplitHorizontal` to use
|
||||
`SplitPane` with that arg set appropriately.
|
||||
* [x] Add a `TerminalParameters` winrt object to `NewTabArgs` and `SplitPane`
|
||||
args. `TerminalParameters` will include the following properties:
|
||||
|
||||
```c#
|
||||
runtimeclass TerminalParameters {
|
||||
String ProfileName;
|
||||
String ProfileGuid;
|
||||
String StartingDirectory;
|
||||
String Commandline;
|
||||
}
|
||||
```
|
||||
- These represent the arguments in `[terminal_parameters]`. When set, they'll
|
||||
both `newTab` and `splitPane` will accept [`profile`, `guid`, `commandline`,
|
||||
`startingDirectory`] as optional parameters, and when they're set, they'll
|
||||
override the default values used when creating a new terminal instance.
|
||||
- `profile` and `guid` will be used to look up the profile to create by
|
||||
`name`, `guid`, respectively, as opposed to the default profile.
|
||||
- The others will override their respective properties from the
|
||||
`TerminalSettings` created for that profile.
|
||||
* [x] Add an optional `"percent"` argument to `SplitPane`, that enables a pane
|
||||
to be split with a specified percent of the parent pane.
|
||||
* [x] Add support to `TerminalApp` for parsing commandline arguments, and
|
||||
constructing a list of `ActionAndArgs` based on those commands.
|
||||
- This will include adding tests that validate a particular commandline
|
||||
generates the given sequence of `ActionAndArgs`.
|
||||
- This will _not_ include _performing_ those actions, or passing the
|
||||
commandline from the `WindowsTerminal` executable to the `TerminalApp`
|
||||
library for parsing. This change does not add any user-facing functional
|
||||
behavior, but is self-contained enough that it can be its own changelist,
|
||||
without depending upon other functionality.
|
||||
* [ ] When parsing a `new-tab` command, configure the `TerminalApp::AppLogic` to
|
||||
set some initial state about itself, to handle the `new-tab` arguments
|
||||
[`--initialPosition`, `--maximized`, `--initialRows`, `--initialCols`]. Only
|
||||
set this state for the first `new-tab` parsed. These settings will overwrite
|
||||
the corresponding global properties on launch.
|
||||
* [ ] When parsing a `help` command or a `list-profiles` command, trigger a
|
||||
event on `AppLogic`. This event should be able to be handled by
|
||||
WindowsTerminal (`AppHost`), and used to display a `MessageBox` with the given
|
||||
text. (see [Potential Issues](##subsystemwindows-or-subsystemconsole) for a
|
||||
discussion on this).
|
||||
* [ ] Add support for performing actions passed on the commandline. This
|
||||
includes:
|
||||
- Passing the commandline into the `TerminalApp` for parsing.
|
||||
- Performing `ActionAndArgs` that are parsed by the Terminal.
|
||||
- At this point, the user should be able to pass the following commands to the
|
||||
Terminal:
|
||||
- `new-tab`
|
||||
- `split-pane`
|
||||
- `move-focus`
|
||||
- `focus-tab`
|
||||
- `open-settings`
|
||||
- `help`
|
||||
- `list-profiles`
|
||||
* [ ] 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 nth `Pane`.
|
||||
- This is in order to support the `-t,--target` parameter of `split-pane`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
As a commandline feature, the accessibility of this feature will largely be tied
|
||||
to the ability of the commandline environment to expose accessibility
|
||||
notifications. Both `conhost.exe` and the Windows Terminal already support
|
||||
basic accessibility patterns, so users using this feature from either of those
|
||||
terminals will be reliant upon their accessibility implementations.
|
||||
|
||||
### Security
|
||||
|
||||
As we'll be parsing user input, that's always subject to worries about buffer
|
||||
length, input values, etc. Fortunately, most of this should be handled for us by
|
||||
the operating system, and passed to us as a commandline via `winMain` and
|
||||
`CommandLineToArgvW`. We should still take extra care in parsing these args.
|
||||
|
||||
### Reliability
|
||||
|
||||
This change should not have any particular reliability concerns.
|
||||
|
||||
### Compatibility
|
||||
|
||||
This change should not regress any existing behaviors.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
This change should not particularly impact startup time or any of these other categories.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
### Commandline escaping
|
||||
|
||||
Escaping commandlines is notoriously tricky to do correctly. Since we're using
|
||||
`;` to delimit commands, which might want to also use `;` in the commandline
|
||||
itself, we'll use `\;` as an escaped `;` within the commandline. This is an area
|
||||
we've been caught in before, so extensive testing will be necessary to make sure
|
||||
this works as expected.
|
||||
|
||||
Painfully, powershell uses `;` as a separator between commands as well. So, if
|
||||
someone wanted to call a `wt` commandline in powershell with multiple commands,
|
||||
the user would need to also escape those semicolons for powershell first. That
|
||||
means a command like ```wt new-tab ; split-pane``` would need to be ```wt new-tab
|
||||
`; split-pane``` in powershell, and ```wt new-tab ; split-pane commandline \; with
|
||||
\; semicolons``` would need to become ```wt new-tab `; split-pane commandline \`;
|
||||
with \`; semicolons```, using ```\`;``` to first escape the semicolon for
|
||||
powershell, then the backslash to escape it for `wt`.
|
||||
|
||||
Alternatively, the user could choose to escape the semicolons with quotes
|
||||
(either single or double), like so: ```wt new-tab ';' split-pane "commandline \;
|
||||
with \; semicolons"```.
|
||||
|
||||
This would get a little ridiculous when using powershell commands that also have
|
||||
semicolons possible escaped within them:
|
||||
|
||||
```powershell
|
||||
wt.exe ";" split-pane "powershell Write-Output 'Hello World' > foo.txt; type foo.txt"
|
||||
```
|
||||
|
||||
We've decided that although this behavior is uncomfortable in powershell, there
|
||||
doesn't seem to be any option out there that's _less_ painful. This is a
|
||||
reasonable option that makes enough logical sense. Users familiar with
|
||||
powershell will understand the need to escape commandlines like this.
|
||||
|
||||
As noted by @jantari:
|
||||
> PowerShell has the --% (stop parsing) operator, which instructs it to stop
|
||||
> interpreting anything after it and just pass it on verbatim. So, the
|
||||
> semicolon-problem could also be addressed by the following syntax:
|
||||
> ```sh
|
||||
> # wt.exe still needs to be interpreted by PowerShell as it's a command in PATH, but nothing after it
|
||||
> wt.exe --% cmd.exe ; split-pane --target-pane 0 -V -% 30 wsl.exe
|
||||
> ```
|
||||
|
||||
### `/SUBSYSTEM:Windows` or `/SUBSYSTEM:Console`?
|
||||
|
||||
When you create an application on Windows, you must link it as either a Windows
|
||||
or a Console application. When the application is launched from a commandline
|
||||
shell as a Windows application, the shell will immediately return to the
|
||||
foreground of the console, which means that any console output emitted by the
|
||||
process will be intermixed with the shell. However, if an application is linked
|
||||
as a Console application, and it's launched from the Start Menu, Run dialog, or
|
||||
any other context that's _not_ a console, then the OS will _automatically_
|
||||
create a console to host the commandline application. That means that briefly, a
|
||||
console window will appear on the screen, even if we decide that we just want to
|
||||
launch our application's window.
|
||||
|
||||
This basically leaves us with two bad scenarios. Either we're a Console
|
||||
application, and a console window always flashes on screen for every
|
||||
non-commandline invocation of the Terminal, or we're a Windows application, and
|
||||
console output we log (including help messages) can get mixed with shell output.
|
||||
Neither of these are particularly good.
|
||||
|
||||
`python` et. al. often ship with _two_ executables, a `python.exe` which is a
|
||||
Console application, and a `pythonw.exe`, which is a Windows application. This
|
||||
however has led to [loads of confusion](https://stackoverflow.com/a/30313091),
|
||||
and even with plentiful documentation, would likely result in users being
|
||||
confused about what does what. For situations like launching the Terminal in the
|
||||
CWD of `explorer.exe`, users would need to use `wtw.exe -d .` to prevent the
|
||||
console window from appearing. However, when calling Windows Terminal from a
|
||||
commandline environment, users who call `wtw.exe /?` would likely get unexpected
|
||||
behavior, because they should have instead called `wt.exe /?`.
|
||||
|
||||
To avoid this confusion, I propose we follow the example of `msiexec /?`. This
|
||||
is a Windows application that uses a `MessageBox` to display its help text.
|
||||
While this is less convenient for users coming exclusively from a commandline
|
||||
environment, it's also the least bad option available to us.
|
||||
* It's less confusing than having control returned to the shell
|
||||
* It's not as bad as forcing the creation of a console window for
|
||||
non-commandline launches.
|
||||
* There's precedent for this kind of dialog (we're not inventing a new pattern
|
||||
here).
|
||||
|
||||
### What happens if `new-tab` isn't the first command?
|
||||
|
||||
Consider the following commandline:
|
||||
|
||||
```sh
|
||||
wt.exe split-pane -V ; new-tab
|
||||
```
|
||||
|
||||
In the future, maybe we could presume in this case that the commands are
|
||||
intended for the current Windows Terminal window, though that's not
|
||||
functionality that will arrive in 1.0. Even when sessions are supported like
|
||||
that, I'm not sure that when we're parsing a commandline, we'll be able to
|
||||
know what session we're currently running in. That might make it challenging to
|
||||
dispatch this kind of command to "the current WT window".
|
||||
|
||||
Additionally, what would happen if this was run in a `conhost` window, that
|
||||
wasn't attached to a Terminal session? We wouldn't be able to tell _the current
|
||||
session_ to `split-pane`, since there wouldn't be one. What would we do then?
|
||||
Display an error message somehow?
|
||||
|
||||
I don't believe that implying the _current Windows Terminal session_ is the
|
||||
correct behavior here. Instead we should either:
|
||||
* Assume that there's an implicit `new-tab` command that's run first, to create
|
||||
the window, _then_ run `split-pane` in that tab.
|
||||
* Immediately display an error that the commandline is invalid, and that a
|
||||
commandline should start with a `new-tab ; `?
|
||||
|
||||
In my initial implementation, I resolved this by assuming there was an implicit
|
||||
`new-tab` command, and that felt right. The team has discussed this, and
|
||||
concluded that's the correct behavior. In the words of @DHowett-MSFT:
|
||||
|
||||
> In favor of "implicit `new-tab`": `wt.exe` without any arguments is _already_
|
||||
> an implicit `new-window` or `new-tab`; we can't claw back the implicitness and
|
||||
> ease of use in that one, so I think in the spirit of keeping that going WT
|
||||
> should automatically do anything necessary to service a command (`wt
|
||||
> split-pane` should operate in a new tab or new window, etc.)
|
||||
|
||||
We should also make sure that when we add support for the `open-settings`
|
||||
command, that command by itself should not imply a `new-tab`. `wt open-settings`
|
||||
should simply open the settings in the user's chosen `.json` editor, without
|
||||
needing to open a terminal window.
|
||||
|
||||
## Future considerations
|
||||
|
||||
* These are some additional argument ideas which are dependent on other features
|
||||
that might not land for a long time. These features were still considered as a
|
||||
part of the design of this solution, though their implementation is purely
|
||||
hypothetical for the time being.
|
||||
* Instead of launching a new Windows Terminal window, attach this new
|
||||
terminal to an existing one. This would require the work outlined in
|
||||
[#2080], so support a "manager" process that could coordinate sessions
|
||||
like this.
|
||||
- This would be something like `wt --session [some-session-id]
|
||||
[commands]`, where `--session [some-session-id]` would tell us that
|
||||
`[more-commands]` are intended for the given other session/window.
|
||||
That way, you could open a new tab in another window with `wt --session
|
||||
0 cmd.exe` (for example).
|
||||
* `list-sessions`: A command to display all the active Windows terminal
|
||||
instances and their session ID's, in a way compatible with the above
|
||||
command. Again, heavily dependent upon the implementation of [#2080].
|
||||
* `--elevated`: Should it be possible for us to request an elevated session
|
||||
of ourselves, this argument could be used to indicate the process should
|
||||
launch in an _elevated_ context. This is considered in pursuit of [#632].
|
||||
* `--file,-f configuration-file`: Used for loading a configuration file to
|
||||
give a list of commands. This file can enable a user to have a re-usable
|
||||
configuration saved somewhere on their machine. When dealing with a file
|
||||
full of startup commands, we'll assume all of them are intended for the
|
||||
given window. So the first `new-tab` in the file will create the window,
|
||||
and all subsequent `new-tab` commands will create tabs in that same
|
||||
window.
|
||||
* In the past we've had requests (like [#756]) for having the terminal start
|
||||
with multiple tabs/panes by default. This might be a path to enabling that
|
||||
scenario. One could imagine the `profiles.json` file including a
|
||||
`defaultConfiguration` property, with a path to a .conf file filled with
|
||||
commands. We'd parse that file on window creation just the same as if it was
|
||||
parsed on the commandline. If the user provides a file on the commandline,
|
||||
we'll just ignore that value from `profiles.json`.
|
||||
* When working on "New Window", we'll want the user to be able to open a new
|
||||
window with not only the default profile, but also a specific profile. This
|
||||
will help us enable that scenario.
|
||||
* We might want to look into `Register‑ArgumentCompleter` in powershell to
|
||||
enable letting the user auto-complete our args in powershell.
|
||||
* If we're careful, we could maybe create short form aliases for all the
|
||||
commands, so the user wouldn't need to type them all out every time. `new-tab`
|
||||
could become `nt`, `split-pane` becomes `sp`, etc. A commandline could look
|
||||
like `wt ; sp less some-log.txt ; fp -t 0` then.
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
Feature Request: wt.exe supports command line arguments (profile, command, directory, etc.) [#607]
|
||||
Add "open Windows terminal here" into right-click context menu [#1060]
|
||||
|
||||
Feature Request: Task Bar jumplist should show items from profile [#576]
|
||||
Draft spec for adding profiles to the Windows jumplist [#1357]
|
||||
|
||||
Spec for tab tear off and default app [#2080]
|
||||
|
||||
[Question] Configuring Windows Terminal profile to always launch elevated [#632]
|
||||
|
||||
New window key binding not working [#2068]
|
||||
|
||||
Feature Request: Start with multiple tabs open [#756]
|
||||
|
||||
<!-- Footnotes -->
|
||||
|
||||
[#756]: https://github.com/microsoft/terminal/issues/756
|
||||
[#576]: https://github.com/microsoft/terminal/issues/576
|
||||
[#607]: https://github.com/microsoft/terminal/issues/607
|
||||
[#632]: https://github.com/microsoft/terminal/issues/632
|
||||
[#1060]: https://github.com/microsoft/terminal/issues/1060
|
||||
[#1357]: https://github.com/microsoft/terminal/pull/1357
|
||||
[#2068]: https://github.com/microsoft/terminal/issues/2068
|
||||
[#2080]: https://github.com/microsoft/terminal/pull/2080
|
||||
[CLI11]: https://github.com/CLIUtils/CLI11
|
||||
@@ -1,717 +0,0 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2019-05-31
|
||||
last updated: 2019-07-31
|
||||
issue id: 754
|
||||
---
|
||||
|
||||
# Cascading Default + User Settings
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec outlines adding support for a cascading settings model. In this model,
|
||||
there are two settings files, instead of one.
|
||||
|
||||
1. The default settings file
|
||||
2. The user's settings file
|
||||
|
||||
The default settings file would be a static, read-only file shipped with the
|
||||
terminal. The user settings file would then contain all the user's chosen
|
||||
customizations to the settings. These two files would then be composed together
|
||||
when the app is launched, so that the runtime settings are the union of both the
|
||||
defaults and whatever modifications the user has chosen. This will enable the
|
||||
app to always use a default schema that it knows will be valid, and minimize the
|
||||
settings that the user needs to customize.
|
||||
|
||||
Should the settings schema ever change, the defaults file will change, without
|
||||
needing to re-write the user's settings file.
|
||||
|
||||
It also outlines a mechanism by which profiles could be dynamically added or
|
||||
hidden from the profiles list, based on some external source.
|
||||
|
||||
## Inspiration
|
||||
|
||||
Largely inspired by the settings model that both VS Code (and Sublime Text) use.
|
||||
|
||||
### Goal: Minimize Re-Serializing `profiles.json`
|
||||
|
||||
We want to re-serialize the user settings file, `profiles.json`, as little as
|
||||
possible. Each time we serialize the file, there's the possibility that we've
|
||||
re-ordered the keys, as `jsoncpp` provides no ordering guarantee of the keys.
|
||||
This isn't great, as each write of the file will randomly re-order the file.
|
||||
|
||||
One of our overarching goals with this change should be to re-serialize the user
|
||||
settings file as little as possible.
|
||||
|
||||
### Goal: Minimize Content in `profiles.json`
|
||||
|
||||
We want the user to only have to make the minimal number of changes possible to
|
||||
the user settings file. Additionally, the user should only have to have the
|
||||
settings that they've changed in that file. If the user wants to change only the
|
||||
`cursorColor` of a profile, they should only need to set that property in the
|
||||
user settings file, and not need an entire copy of the `Profile` object in their
|
||||
user settings file. That would create additional noise that's not relevant to
|
||||
the user.
|
||||
|
||||
### Goal: Remove the Need to Reset Settings Entirely to get New Settings
|
||||
One problem with the current settings design is that we only generate "default"
|
||||
settings for the user when there's no settings file present at all. So, when we
|
||||
want to do things like update the default profiles to have an icon, or add
|
||||
support for generating WSL profiles, it will only apply to users for fresh
|
||||
installs. Otherwise, a user needs to completely delete the settings file to have
|
||||
the terminal re-generate the default settings.
|
||||
|
||||
This is fairly annoying to the end-user, so ideally we'll find a way to be able
|
||||
to prevent this scenario.
|
||||
|
||||
### Goal: Prevent Roaming Settings from Failing
|
||||
Another problem currently is that when settings roam to another machine, it's
|
||||
possible that the second machine doesn't have the same applications installed as
|
||||
the first, and some profiles might be totally invalid on the second machine.
|
||||
Take for example, profiles for WSL distros. If you have and Ubuntu profile on
|
||||
your first machine, and roam that profile to a second machine without Ubuntu
|
||||
installed, then the Ubuntu profile would be totally broken on the second
|
||||
machine.
|
||||
|
||||
While we won't be able to non-destructively prevent all failures of this case,
|
||||
we should be able to catch it in certain scenarios.
|
||||
|
||||
## Solution Design
|
||||
|
||||
The settings are now composed from two files: a "Default" settings file, and a
|
||||
"User" settings file.
|
||||
|
||||
When we load the settings, we'll perform the following steps, each mentioned in
|
||||
greater detail below:
|
||||
1. Load from disk the `defaults.json` (the default settings) -> DefaultsJson
|
||||
1. Load from disk the `profiles.json` (the user settings) -> UserJson
|
||||
1. Parse DefaultsJson to create all the default profiles, schemes, keybindings.
|
||||
1. [Not covered in this spec] Check the UserJson to find the list of dynamic
|
||||
profile sources that should run.
|
||||
1. Run all the _enabled_ dynamic profile generators. Those profiles will be
|
||||
added to the set of profiles.
|
||||
- During this step, check if any of the profiles added here don't exist in
|
||||
UserJson. If they _don't_, the generator created a profile that didn't
|
||||
exist before. Return a value indicating the user settings should be
|
||||
re-saved (with the new profiles added).
|
||||
1. [Not covered in this spec] Layer the UserJson.globals.defaults settings to
|
||||
every profile in the set, both the defaults, and generated profiles.
|
||||
1. Apply the user settings from UserJson. Layer the profiles on top of the
|
||||
existing profiles if possible (if both `guid` and `source` match). If a
|
||||
profile from the user settings does not already exist, make sure to apply the
|
||||
UserJson.globals.defaults settings first. Also layer Color schemes and
|
||||
keybindings.
|
||||
- If a profile has a `source` key, but there is not an existing profile with
|
||||
a matching `guid` and `source`, don't create a new Profile object for it.
|
||||
Either that generator didn't run, or the generator wanted to delete that
|
||||
profile, so we'll effectively hide the profile.
|
||||
1. Re-order the list of profiles, to match the ordering in the UserJson. If a
|
||||
profile doesn't exist in UserJson, it should follow all the profiles in the
|
||||
UserJson. If a profile listed in UserJson doesn't exist, we can skip it
|
||||
safely in this step (the profile will be a dynamic profile that didn't get
|
||||
populated.)
|
||||
1. Validate the settings.
|
||||
1. If requested in step 5, write the modified settings back to `profiles.json`.
|
||||
|
||||
### Default Settings
|
||||
|
||||
We'll have a static version of the "Default" file **hardcoded within the
|
||||
application package**. This `defaults.json` file will live within the
|
||||
application's package, which will prevent users from being able to edit it.
|
||||
|
||||
```json
|
||||
// This is an auto-generated file. Place any modifications to your settings in "profiles.json"
|
||||
```
|
||||
|
||||
This disclaimer will help identify that the file shouldn't be modified. The file
|
||||
won't actually be generated, but because it's shipped with our app, it'll be
|
||||
overridden each time the app is updated. "Auto-generated" should be good enough
|
||||
to indicate to users that it should not be modified.
|
||||
|
||||
Because the `defaults.json` file is hardcoded within our application, we can use
|
||||
its text directly, without loading the file from disk. This should help save
|
||||
some startup time, as we'll only need to load the user settings from disk.
|
||||
|
||||
When we make changes to the default settings, or we make changes to the settings
|
||||
schema, we should make sure that we update the hardcoded `defaults.json` with
|
||||
the new values. That way, the `defaults.json` file will always have the complete
|
||||
set of settings in it.
|
||||
|
||||
### Layering settings
|
||||
|
||||
When we load the settings, we'll do it in three stages. First, we'll deserialize
|
||||
the default settings that we've hardcoded. We'll then generate any profiles that
|
||||
might come from dynamic profile sources. Then, we'll intelligently layer the
|
||||
user's setting upon those we've already loaded. If a user wants to make changes
|
||||
to some objects, like the default profiles, we'll need to make sure to load from
|
||||
the user settings into the existing objects we created from the default
|
||||
settings.
|
||||
|
||||
* We'll need to make sure that any profile in the user settings that has a GUID
|
||||
matching a default profile loads the user settings into the object created
|
||||
from the defaults.
|
||||
* We'll need to make sure that there's only one action bound to each key chord
|
||||
for a keybinding. If there are any key chords in the user settings that match
|
||||
a default key chord, we should bind them to the action from the user settings
|
||||
instead.
|
||||
* For any color schemes whose name matches the name of a default color scheme,
|
||||
we'll need to apply the user settings to the existing color scheme. For
|
||||
example, a user could override the `red` entry of the "Campbell" scheme to be
|
||||
`#ff9900` if they want. This would then apply to all profiles using the
|
||||
"Campbell" scheme.
|
||||
* For profiles that were created from a dynamic profile source, they'll have
|
||||
both a `guid` and `source` guid that must _both_ match. If a user profile with
|
||||
a `source` set does not find a matching profile at load time, the profile will
|
||||
be ignored. See more details in the [Dynamic Profiles](#dynamic-profiles)
|
||||
section.
|
||||
|
||||
### Hiding Default Profiles
|
||||
|
||||
What if a user doesn't want to see one of the profiles that we've included in
|
||||
the default profiles?
|
||||
|
||||
We will add a `hidden` key to each profile, which defaults to false. When we
|
||||
want to mark a profile as hidden, we'd just set that value to `true`, instead of
|
||||
trying to look up the profile's guid.
|
||||
|
||||
So, if someone wanted to hide the default cmd.exe profile, all they'd have to do
|
||||
is add `"hidden": true` to the cmd.exe entry in their user settings, like so:
|
||||
|
||||
```js
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
// Make changes here to the cmd.exe profile
|
||||
"guid": "{6239a42c-1de4-49a3-80bd-e8fdd045185c}",
|
||||
"hidden": true
|
||||
}
|
||||
],
|
||||
```
|
||||
|
||||
#### Hidden Profiles and the Open New Tab shortcuts
|
||||
|
||||
Currently, there are keyboard shortcuts for "Open New Tab With Profile
|
||||
<N>". These shortcuts will open up the Nth profile in the new tab
|
||||
dropdown. Considering we're adding the ability to remove profiles from that
|
||||
list, but keep them in the overall list of profiles, we'll need to make sure
|
||||
that the handler for that event still opens the Nth _visible_ profile.
|
||||
|
||||
### Serializing User Settings
|
||||
|
||||
How can we tell that a setting should be written back to the user settings file?
|
||||
|
||||
If the value of the setting isn't the same as the defaults, then it could easily
|
||||
be added to the user's `profiles.json`. We'll have to do a smart serialization
|
||||
of the various settings models. We'll pass in the default version **of that
|
||||
model** during the serialization. If that object finds that a particular setting
|
||||
is the same as a default setting, then we'll skip serializing it.
|
||||
|
||||
What happens if a user has chosen to set the value to _coincidentally_ the same
|
||||
value as the default value? We should keep that key in the user's settings file,
|
||||
even though it is the same.
|
||||
|
||||
In order to facilitate this, we'll need to keep the originally parsed user
|
||||
settings around in memory. When we go to serialize the settings, we'll check if
|
||||
either the setting exists already in the user settings file, or the setting has
|
||||
changed. If either is true, then we'll make sure to write that setting back out.
|
||||
|
||||
For serializing settings for the default profiles, we'll check if the setting is
|
||||
in the user settings file, or if the value of the setting is different from the
|
||||
version of that `Profile` from the default settings. For user-created profiles,
|
||||
we'll compare the value of the setting with the value of the _default
|
||||
constructed_ `Profile` object. This will help ensure that each profile in the
|
||||
user's settings file maintains the minimal amount of info necessary.
|
||||
|
||||
When we're adding profiles due to their generation in a dynamic profile
|
||||
generator, we'll need to serialize them, then insert them back into the
|
||||
originally parsed json object to be serialized. We don't want the automatic
|
||||
creation of a new profile to automatically trigger re-writing the entire user
|
||||
settings file, but we do want newly created dynamic profiles to have an entry
|
||||
the user can easily edit.
|
||||
|
||||
### Dynamic Profiles
|
||||
|
||||
Sometimes, we may want to auto-generate a profile on the user's behalf. Consider
|
||||
the case of WSL distros on their machine, or VMs running in Azure they may want
|
||||
to auto-connect to. These _dynamic_ profiles have a source that might be added
|
||||
or removed after the app is installed, and they will be different from user to
|
||||
user.
|
||||
|
||||
Currently, these profiles are only generated when a user first launches the
|
||||
Terminal. If they already have a `profiles.json` file, then we won't run the
|
||||
auto-generation behavior. This is obviously not great - if any new types of
|
||||
dynamic profiles are added, then users that already have the Terminal installed
|
||||
won't get any of these dynamic profiles. Furthermore, if any of the sources of
|
||||
these dynamic profiles are removed, then the app won't auto-remove the
|
||||
associated profile.
|
||||
|
||||
In the new model, with a combined defaults & user settings, how should these
|
||||
dynamic profiles work?
|
||||
|
||||
I propose we add functionality to automatically search for these profile sources
|
||||
and add/remove them on _every_ Terminal launch. To make this functionality work
|
||||
appropriately, we'll need to introduce a constraint on dynamic profiles.
|
||||
|
||||
**For any dynamic profiles, they must be able to be generated using a stable
|
||||
GUID**. For example, any time we try adding the "Ubuntu" profile, we must be
|
||||
able to generate the same GUID every time. This way, when a dynamic profile
|
||||
generator runs, it can check if that profile source already has a profile
|
||||
associated with it, and do nothing (as to not create many duplicate "Ubuntu"
|
||||
profiles, for example).
|
||||
|
||||
Additionally, each dynamic profile generator **must have a unique source guid**
|
||||
to associate with the profile. When a dynamic profile is generated, the source's
|
||||
guid will be added to the profile, to make sure the profile is correlated with
|
||||
the source it came from.
|
||||
|
||||
We'll generate these dynamic profiles immediately after parsing the default
|
||||
profiles and settings. When a generator runs, it'll be able to create unique
|
||||
profile GUIDs for each source it wants to generate a profile for. It'll hand
|
||||
back a list of Profile objects, with settings set up how the generator likes,
|
||||
with GUIDs set.
|
||||
|
||||
After a dynamic profile generator runs, we will determine what new profiles need
|
||||
to be added to the user settings, so we can append those to the list of
|
||||
profiles. The deserializer will look at the list of generated profiles and check
|
||||
if each and every one already has a entry in the user settings. The generator
|
||||
will just blind hand back a list of profiles, and the deserializer will figure
|
||||
out if any of them need to be added to the user settings. We'll store some sort
|
||||
of result indicating that we want a save operation to occur. After the rest of
|
||||
the deserializing is done, the app will then save the `profiles.json` file,
|
||||
including these new profiles.
|
||||
|
||||
When we're serializing the settings, instead of comparing a dynamic profile to
|
||||
the default-constructed `Profile`, we'll compare it to the state of the
|
||||
`Profile` after the dynamic profile generator created it. It'd then only
|
||||
serialize settings that are different from the auto-generated version. It will
|
||||
also always make sure that the `guid` of the dynamic profile is included in the
|
||||
user settings file, as a point for the user to add customizations to the dynamic
|
||||
profile to. Additionally, we'll also make sure the `source` is always serialized
|
||||
as well, to keep the profile correlated with the generator that created it.
|
||||
|
||||
We'll need to keep the state of these dynamically generated profiles around in
|
||||
memory during runtime to be able to ensure the only state we're serializing is
|
||||
that which is different from the initially generated dynamic profile.
|
||||
|
||||
When the generator is run, and determines that a new profile has been added,
|
||||
we'll need to make sure to add the profile to the user's settings file. This
|
||||
will create an easy point for users to customize the dynamic profiles. When
|
||||
added to the user settings, all that will be added is the `name`, `guid`, and
|
||||
`source`.
|
||||
|
||||
Additionally, a user might not want a dynamic profile generator to always run.
|
||||
They might want to keep their Azure connections visible in the list of profiles,
|
||||
even if its no longer a valid target. Or they might want to not automatically
|
||||
connect to Azure to find new instances every time they launch the terminal. To
|
||||
enable scenarios like this, we'll add an additional setting,
|
||||
`disabledProfileSources`. This is an array of guids. If any guids are in that
|
||||
list, then those dynamic profile generators _won't_ be run, suppressing those
|
||||
profiles from appearing in the profiles list.
|
||||
|
||||
If a dynamic profile generator needs to "delete" a profile, this will also work
|
||||
naturally with the above rules. Lets examine the case where the user has
|
||||
uninstalled the Ubuntu distro. When the WSL generator runs, it won't create the
|
||||
Ubuntu profile. When we get to the Ubuntu profile in the user's settings, it'll
|
||||
have a `source`, but we won't already have a profile with that `guid` and
|
||||
`source`. So we'll just ignore it, because whatever source for that profile
|
||||
doesn't want it anymore. Effectively, this will act like it was "deleted",
|
||||
though the artifacts still remain untouched in the user's json.
|
||||
|
||||
#### What if a dynamic profile is removed, but it's the default?
|
||||
|
||||
I'll direct our attention to [#1348] - Display a specific error for not finding
|
||||
the default profile. When we're done loading, and we determine that the default
|
||||
profile doesn't exist in the finalized list of profiles, we'll display a dialog
|
||||
to the user. This includes both hidden profiles and dynamic profiles that have
|
||||
been "deleted". We'll temporarily use the _first_ profile instead.
|
||||
|
||||
#### Dynamic profile GUID generation
|
||||
|
||||
In order to help facilitate the generation of stable, unique GUIDs for
|
||||
dynamically generated profiles, we'll enforce a few methods on each generator.
|
||||
The Generator should implement a method that returns its _unique_ namespace for
|
||||
profiles it generates:
|
||||
|
||||
```c++
|
||||
class IDynamicProfileGenerator
|
||||
{
|
||||
...
|
||||
virtual std::wstring GetNamespace() = 0;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
For example, the WSL generator would return `Microsoft.Terminal.WSL`. The
|
||||
Powershell Core generator would return `Microsoft.Terminal.PowershellCore`.
|
||||
We'll use these names to be able to generate uuidv5 GUIDs that will be unique
|
||||
(so long as the names are unique).
|
||||
|
||||
The generator should also be able to ask the app for two other pieces of
|
||||
functionality:
|
||||
* The generator should be able to ask the app for the generator's own namespace
|
||||
GUID
|
||||
* The generator should be able to ask the app for a uuidv5 in the generator's
|
||||
namespace, given a specific name key.
|
||||
|
||||
These two functions will be exposed to the generator like so:
|
||||
|
||||
```c++
|
||||
GUID GetNamespaceGuid(IDynamicProfileGenerator& generator);
|
||||
GUID GetGuidForName(IDynamicProfileGenerator& generator, std::wstring& name);
|
||||
```
|
||||
|
||||
The generator does not _need_ to use `GetGuidForName` to generate guids for it's
|
||||
profiles. If the generator can determine another way to generate stable GUIDs
|
||||
for its profiles, it's free to use whatever method it wants. `GetGuidForName` is
|
||||
provided as a convenience.
|
||||
|
||||
It's not the responsibility of the dynamic profile generator to fill in the
|
||||
`source` of the profiles it generates. The deserializer will make sure to go
|
||||
through and fill in the guid for the generated profiles given the generator's
|
||||
namespace GUID.
|
||||
|
||||
### Powershell Core & the Defaults
|
||||
|
||||
How do we handle the potential existence of Powershell Core in this model?
|
||||
Powershell core is unique as far as the default profiles goes - it may or may
|
||||
not exist on the user's system. Not only that, but depending on the user's
|
||||
install of Powershell Core, it might have a path in either `Program Files` or
|
||||
`Program Files(x86)`.
|
||||
|
||||
Additionally, if it _is_ installed, we set it as the default profile instead of
|
||||
Windows Powershell.
|
||||
|
||||
Powershell core acts much like a dynamic profile. It has an installation source
|
||||
that may or not be there. So we'll add a dynamic profile generator for
|
||||
Powershell Core. This will automatically create a profile for Powershell Core if
|
||||
necessary.
|
||||
|
||||
Unlike the other dynamic profiles, if Powershell Core is present on
|
||||
_first_ launch of the terminal, we set that as the default profile. This can
|
||||
still be done - we'll need to do some special-case work when we're loading the
|
||||
user settings and we _don't_ find any existing settings. When that happens,
|
||||
we'll generate all the default user settings. Before we commit them, we'll check
|
||||
if the Powershell Core profile exists, and if it does, we'll set that as the
|
||||
default profile before writing the settings to disk.
|
||||
|
||||
### Unbinding a Keybinding
|
||||
|
||||
How can a user unbind a key that's part of the default keybindings? What if a
|
||||
user really wants <kbd>ctrl</kbd>+<kbd>t</kbd> to fall through to the
|
||||
commandline application attached to the shell, instead of opening a new tab?
|
||||
|
||||
We'll need to introduce a new keybinding command that should indicate that the
|
||||
key is unbound. We'll load the user keybindings and layer them on the defaults
|
||||
as described above. If during the deserializing we find an entry that's bound to
|
||||
the command `"unbound"` or any other string that we don't understand, instead of
|
||||
trying to _set_ the keybinding, we'll _clear_ the keybinding with a new method
|
||||
`AppKeyBindings::ClearKeyBinding(chord)`.
|
||||
|
||||
### Removing the Globals Object
|
||||
|
||||
As a part of #[1005](https://github.com/microsoft/terminal/pull/1005), all the
|
||||
global settings were moved to their own object within the serialized settings.
|
||||
This was to try and make the file easier to parse as a user, considering global
|
||||
settings would be intermingled with profiles, keybindings, color schemes, etc.
|
||||
Since this change will make the user settings dramatically easier to navigate,
|
||||
we should probably remove the `globals` object, and have globals at the root
|
||||
level again.
|
||||
|
||||
### Default `profiles.json`
|
||||
|
||||
Below is an example of what the default user settings file might look like when
|
||||
it's first generated, taking all the above points into consideration.
|
||||
|
||||
```js
|
||||
// To view the default settings, open <path-to-app-package>\defaults.json
|
||||
{
|
||||
"defaultProfile" : "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
|
||||
"profiles": [
|
||||
{
|
||||
// Make changes here to the cmd.exe profile
|
||||
"guid": "{6239a42c-1de4-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
{
|
||||
// Make changes here to the Windows Powershell profile
|
||||
"guid": "{086a83cd-e4ef-418b-89b1-3f6523ff9195}",
|
||||
},
|
||||
{
|
||||
"guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
|
||||
"name" : "Powershell Core",
|
||||
"source": "{2bde4a90-d05f-401c-9492-e40884ead1d8}",
|
||||
}
|
||||
],
|
||||
|
||||
// Add custom color schemes to this array
|
||||
"schemes": [],
|
||||
|
||||
// Add any keybinding overrides to this array.
|
||||
// To unbind a default keybinding, set the command to "unbound"
|
||||
"keybindings": []
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Note the following:
|
||||
* cmd.exe and powershell.exe are both in the file, as to give users an easy
|
||||
point to extend the settings for those default profiles.
|
||||
* Powershell Core is included in the file, and the default profile has been set
|
||||
to its GUID. The `source` has been set, indicating that it came from a dynamic profile source.
|
||||
* There are a few helpful comments scattered throughout the file to help point
|
||||
the user in the right direction.
|
||||
|
||||
### Re-ordering profiles
|
||||
|
||||
Since there are shortcuts to open the Nth profile in the list of profiles, we
|
||||
need to expose a way for the user to change the order of the profiles. This was
|
||||
not a problem when there was only a single list of profiles, but if the defaults
|
||||
are applied _first_ to the list of profiles, then the user wouldn't be able to
|
||||
change the order of the default profiles. Additionally, any profiles they add
|
||||
would _always_ show up after the defaults.
|
||||
|
||||
To remedy this, we could scan the user profiles in the user settings first, and
|
||||
create `Profile` objects for each of those profiles first. These `Profile`s
|
||||
would only be initialized with their GUID temporarily, but they'd be placed into
|
||||
the list of profiles in the order they appear in the user's settings. Then, we'd
|
||||
load all the default settings, overlaying any default profiles on the `Profile`
|
||||
objects that might already exist in the list of profiles. If there are any
|
||||
default profiles that don't appear in the user's settings, they'll appear
|
||||
_after_ any profiles in the user's settings. Then, we'll overlay the full user
|
||||
settings on top of the defaults.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
### Opening `defaults.json`
|
||||
How do we open both these files to show to the user (for the interim period
|
||||
before a proper Settings UI is created)? Currently, the "Settings" button only
|
||||
opens a single json file, `profiles.json`. We could keep that button doing the
|
||||
same thing, though we want the user to be able to also view the default settings
|
||||
file, to be able to inspect what settings they wish to change.
|
||||
|
||||
We could have the "Settings" button open _both_ files at the same
|
||||
time. I'm not sure that `ShellExecute` (which is used to open these files)
|
||||
provides any ordering guarantees, so it's possible that the `defaults.json`
|
||||
would open in the foreground of the default json editor, while making in unclear
|
||||
that there's another file they should be opening instead. Additionally, if
|
||||
there's _no_ `.json` editor for the user, I believe the shell will attempt
|
||||
_twice_ to ask the user to select a program to open the file with, and it might
|
||||
not be clear that they need to select a program in both dialogs.
|
||||
|
||||
Alternatively, we could make the defaults file totally inaccessible from the
|
||||
Terminal UI, and instead leave a comment in the auto-generated `profiles.json`
|
||||
like so:
|
||||
|
||||
```json
|
||||
// To view the default settings, open the defaults.json file in this directory
|
||||
```
|
||||
|
||||
The "Settings" button would then only open the file the user needs to edit, and
|
||||
provide them instructions on how to open the defaults file.
|
||||
|
||||
There could alternatively be a hidden option for the "Open Settings" button,
|
||||
where holding <kbd>Alt</kbd> while clicking on the button would open the
|
||||
`defaults.json` instead.
|
||||
|
||||
We could additionally add a `ShortcutAction` (to be bound to a keybinding) that
|
||||
would `openDefaultSettings`, and we could bind that to
|
||||
<kbd>ctrl</kbd>+<kbd>alt</kbd>+<kbd>\`</kbd>, similar to `openSettings` on
|
||||
<kbd>ctrl</kbd>+<kbd>\`</kbd>.
|
||||
|
||||
### How does this work with the settings UI?
|
||||
|
||||
If we only have one version of the settings models (Globals, Profiles,
|
||||
ColorSchemes, Keybindings) at runtime, and the user changes one of the settings
|
||||
with the settings UI, how can we tell that settings changed?
|
||||
|
||||
Fortunately, this should be handled cleanly by the algorithm proposed above, in
|
||||
the "Serializing User Settings" section. We'll only be serializing settings that
|
||||
have changed from the defaults, so only the actual changes they've made will be
|
||||
persisted back to the user settings file.
|
||||
|
||||
## Capabilities
|
||||
### Security
|
||||
|
||||
I don't think this will introduce any new security issues that weren't already
|
||||
present
|
||||
|
||||
### Reliability
|
||||
I don't think this will introduce any new reliability concerns that weren't
|
||||
already present. We will likely improve our reliability, as dynamic profiles
|
||||
that no longer exist will not cause the terminal to crash on startup anymore.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
By not writing the defaults to disk, we'll theoretically marginally improve the
|
||||
load and save times for the `profiles.json` file, by simply having a smaller
|
||||
file to load. However we'll also be doing more work to process the layering of
|
||||
defaults and user settings, which will likely slightly increase the load times.
|
||||
Overall, I expect the difference to be negligible due to these factors.
|
||||
|
||||
One potential concern is long-running dynamic profile generators. Because
|
||||
they'll need to run on startup, they could negatively impact startup time. You
|
||||
can read more below, in "Dynamic Profile Generators Need to be Enabled".
|
||||
|
||||
### Accessibility
|
||||
N/A
|
||||
|
||||
## Potential Issues
|
||||
|
||||
### Profiles with the same `guid` as a dynamic profile but not the same `source`
|
||||
|
||||
What happens if the User settings has a profile with a `guid` that matches a
|
||||
dynamic or default profile, but the user profile doesn't have a matching source?
|
||||
This could happen trivially easily if the user deletes the `source` key from a
|
||||
profile that has dynamically generated.
|
||||
|
||||
We could:
|
||||
1. Treat the profile as an entirely separate profile
|
||||
- There's lots of other code that assumes each profile has only a unique GUID,
|
||||
so we'd have to change the GUID of this profile. This would mean writing out
|
||||
the user settings, which we'd like to avoid.
|
||||
- We'll still end up generating the entry for the dynamic profile in the
|
||||
user's settings, so we'll need to write out the user settings anyways.
|
||||
- This other profile will likely not have a commandline set, so it might not
|
||||
work at all.
|
||||
1. Ignore the profile entirely.
|
||||
- When the dynamic profile generator runs, we're not going to find another
|
||||
entry in the user profiles with both a matching `guid` and a matching
|
||||
`source`. So we'll end up creating _another_ entry in the user profiles for
|
||||
the dynamic profile.
|
||||
- How could the user know that the profile is being ignored? There's nothing
|
||||
in the file itself that indicates obviously that this profile is now
|
||||
invalid.
|
||||
1. Treat the user settings as part of the dynamic profile
|
||||
- In this scenario, the user profile continues to exist as part of the dynamic profile.
|
||||
- When the dynamic profile generator runs, we're not going to find another
|
||||
entry in the user profiles with both a matching `guid` and a matching
|
||||
`source`. So we'll end up creating _another_ entry in the user profiles for
|
||||
the dynamic profile.
|
||||
- These two entries will each be layered upon the dynamically generated
|
||||
profile, so the settings in the second profile entry will override
|
||||
settings from the first.
|
||||
- If the user disables the generator, or the profile source is removed, the
|
||||
dynamic profile will cease to exist. However, the profile without the
|
||||
`source` entry will remain, though likely will not work.
|
||||
- How do we order these profiles for the user? When we're parsing the user
|
||||
profiles list to build an ordering of profiles, do we use the first entry as
|
||||
the index for that profile?
|
||||
1. (Variant of the above) Treat the profile as part of the dynamic profile, and
|
||||
re-insert the `source` key.
|
||||
- This will re-connect the user profile to the dynamic one.
|
||||
- We'll need to make sure to do this before determining the new dynamic
|
||||
profiles to add to the user settings.
|
||||
- Given all the scenarios are going to cause a user settings write anyways,
|
||||
this isn't terrible.
|
||||
- If the user _really_ wants to split the profile in their user settings from
|
||||
the dynamic one, they're free to always generate a new guid _and_ delete the
|
||||
`source` key.
|
||||
|
||||
Given the drawbacks associated with options 1-3, I propose we choose option 4 as
|
||||
our solution to this case.
|
||||
|
||||
### Migrating Existing Settings
|
||||
|
||||
I believe that existing `profiles.json` files will smoothly update to this
|
||||
model, without breaking. While in the new model, the `profiles.json` file can be
|
||||
much more sparse, users who have existing `profiles.json` files will have full
|
||||
settings in their user settings. We'll leave their files largely untouched, as
|
||||
we won't touch keys that have the same values as defaults that are currently in
|
||||
the `profiles.json` file. Fortunately though, users should be able to remove
|
||||
much of the boilerplate from their `profiles.json` files, and trim it down just
|
||||
to their modifications.
|
||||
|
||||
#### Migrating Powershell Core
|
||||
|
||||
Right now, default-generated Powershell Core profiles exist with a stable guid
|
||||
we've generated for them. However, when we move Powershell Core to being a
|
||||
dynamically generated profile, we'll have to ensure that we don't create a
|
||||
duplicated "dynamic" entry for that profile. If we want to convert the existing
|
||||
Powershell Core profiles into a dynamic profile, we'll need to make sure to add
|
||||
a `source` key to the profile. Everything else in the profile can remain the
|
||||
same. Once the `source` is added, we'll know to treat it as a dynamic profile,
|
||||
and it'll respond dynamically.
|
||||
|
||||
This is actually something that will automatically be covered by the scenario
|
||||
mentioned above in "Profiles with the same `guid` as a dynamic profile but not
|
||||
the same `source`". When we encounter the existing Powershell Core profiles that
|
||||
don't have a `source`, we'll automatically think they're the dynamically
|
||||
generated ones, and auto-migrate them.
|
||||
|
||||
#### Migrating Existing WSL Profiles
|
||||
|
||||
Similar to the above, so long as we ensure the WSL dynamic profile generator
|
||||
generates the _same_ GUIDs as it does currently, all the existing WSL profiles
|
||||
will automatically be migrated to dynamic profiles.
|
||||
|
||||
### Dynamic Profile Generators Need to be Enabled
|
||||
With the current proposal, profiles that are generated by a dynamic profile
|
||||
generator _need_ that generator to be enabled for the profile to appear in the
|
||||
list of profiles. If the generator isn't enabled, then the important parts of
|
||||
the profile (name, commandline) will never be set, and the profile's settings
|
||||
from the user settings will be ignored at runtime.
|
||||
|
||||
For generators where the generation of profiles might be a lengthy process, this
|
||||
could negatively impact startup time. Take for example, some hypothetical
|
||||
generator that needs to make web requests to generate dynamic profiles. Because
|
||||
we need the finalized settings to be able to launch the terminal, we'll be stuck
|
||||
loading until that generator is complete.
|
||||
|
||||
However, if the user disables that generator entirely, we'll never display that
|
||||
profile to the user, even if they've done that setup before.
|
||||
|
||||
So the trade-off with this design is that non-existent dynamic profiles will
|
||||
never roam to machines where they don't exist and aren't valid, but the
|
||||
generators _must_ be enabled to use the dynamic profiles.
|
||||
|
||||
## Future considerations
|
||||
* It's possible that a very similar layering loading mechanism could be used to
|
||||
layer per-machine settings with roaming settings. Currently, there's only one
|
||||
settings file, and it roams to all your devices. This could be problematic,
|
||||
for example, if one of your machines has a font installed, but another
|
||||
doesn't. A proposed solution to that problem was to have both roaming settings
|
||||
and per-machine settings. The code to layer settings from the defaults and the
|
||||
user settings could be re-used to handle layer the roaming and per-machine
|
||||
settings.
|
||||
* What if an extension wants to generate their own dynamic profiles? We've
|
||||
already outlined a contract that profile generators would have to follow to
|
||||
behave correctly. It's possible that we could abstract our implementation into
|
||||
a WinRT interface that extensions could implement, and be triggered just like
|
||||
other dynamic profile generators.
|
||||
* **Multiple settings files** - This could enable us to place color schemes into
|
||||
a separate file (like `colorschemes.json`) and put keybindings into their own
|
||||
file as well, and reduce the number of settings in the user's `profiles.json`.
|
||||
It's unclear if this is something that we need quite yet, but the same
|
||||
layering functionality that enables this scenario could also enable more than
|
||||
two sources for settings.
|
||||
* **Global Default Profile Settings** - Say a user wants to override what the
|
||||
defaults for a profile are, so that they can set settings for _all_ their
|
||||
profiles at once? We could maybe introduce a profile in the user settings file
|
||||
with a special guid set to `"default`, that we look for first, and treat
|
||||
specially. We wouldn't include it in the list of profiles. When we're creating
|
||||
profiles, we'll start with that profile as our prototype, instead of using the
|
||||
default-constructed `Profile`. When we're serializing profiles, we'd again use
|
||||
that as the point of comparison to check if a setting's value has changed.
|
||||
There may be more unknowns with this proposal, so I leave it for a future
|
||||
feature spec.
|
||||
- We'll also want to make sure that when we're serializing default/dynamic
|
||||
profiles, we take into account the state from the global defaults, and we
|
||||
don't duplicate that information into the entries for those types of profiles
|
||||
in the user profiles.
|
||||
* **Re-ordering profiles** - Under "Solution Design", we provide an algorithm
|
||||
for decoding the settings. One of the steps mentioned is parsing the user
|
||||
settings to determine the ordering of the profiles. It's possible in the
|
||||
future we may want to give the user more control over this ordering. Maybe
|
||||
we'll want to allow the user to manually index the profiles. Or, as discussed
|
||||
in issues like #1571, we may want to allow the user to further customize the
|
||||
new tab dropdown, beyond just the order of profiles. The re-ordering step
|
||||
would be a great place to add code to support this re-ordering, with whatever
|
||||
algorithm we eventually land on. Determining such an algorithm is outside the
|
||||
scope of this spec, however.
|
||||
|
||||
## Resources
|
||||
N/A
|
||||
|
||||
|
||||
<!-- Footnotes -->
|
||||
|
||||
[#1348]: https://github.com/microsoft/terminal/issues/1348
|
||||
@@ -1,255 +0,0 @@
|
||||
---
|
||||
author: James Holderness @j4james
|
||||
created on: 2019-07-17
|
||||
last updated: 2019-07-28
|
||||
issue id: 976
|
||||
---
|
||||
|
||||
# VT52 Escape Sequences
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec outlines the work required to split off the existing VT52 commands from the VT100 implementation, and extend the VT52 support to cover all of the core commands.
|
||||
|
||||
## Inspiration
|
||||
|
||||
The existing VT52 commands aren't currently implemented as a separate mode, so they conflict with sequences defined in the VT100 specification. This is blocking us from adding support for the VT100 Index (IND) escape sequence, which is one of the missing commands required to pass the test of cursor movements in Vttest.
|
||||
|
||||
## Solution Design
|
||||
|
||||
The basic idea is to add support for the [DECANM private mode sequence](https://vt100.net/docs/vt100-ug/chapter3.html#DECANM), which can then be used to switch from the default _ANSI_ mode, to a new _VT52_ mode. Once in _VT52_ mode, there is a separate [_Enter ANSI Mode_ sequence](https://vt100.net/docs/vt100-ug/chapter3.html#VT52ANSI) (`ESC <`) to switch back again.
|
||||
|
||||
In terms of implementation, there are a number of areas of the system that would need to be updated.
|
||||
|
||||
### The State Machine
|
||||
|
||||
In order to implement the VT52 compatibility mode correctly, we'll need to introduce a flag in the `StateMachine` class that indicates the mode that is currently active. When in VT52 mode, certain paths in the state diagram should not be followed - for example, you can't have CSI, OSC, or SS3 escape sequences. There would also need to be an additional state to handle VT52 parameters (for the _Direct Cursor Address_ command). These parameters take a different form to the typical VT100 parameters, as they follow the command character instead of preceding it.
|
||||
|
||||
It would probably be best to introduce a new dispatch method in the `IStateMachineEngine` interface to handle the parsed VT52 sequences, since the existing `ActionEscDispatch` does not support parameters (which are required for the _Direct Cursor Address_ command). I think it would also make for a cleaner implementation to have the VT52 commands separate from the VT100 code, and would likely have less impact on the performance that way.
|
||||
|
||||
### The Terminal Input
|
||||
|
||||
The escape sequences generated by the keyboard for function keys, cursor keys, and the numeric keypad, are not the same in VT52 mode as they are in ANSI mode. So there would need to be a flag in the `TerminalInput` class to keep track of the current mode, and thus be able to generate the appropriate sequences for that mode.
|
||||
|
||||
Technically the VT52 keyboard doesn't map directly to a typical PC keyboard, so we can't always work from the specs in deciding what sequences are required for each key. When in doubt, we should probably be trying to match the key sequences generated by XTerm. The sequences below are based on the default XTerm mappings.
|
||||
|
||||
**Function Keys**
|
||||
|
||||
The functions keys <kbd>F1</kbd> to <kbd>F4</kbd> generate a simple ESC prefix instead of SS3 (or CSI). These correspond with the four function keys on the VT100 keypad. In V52 mode they are not affected by modifiers.
|
||||
|
||||
Key | ANSI mode | VT52 mode
|
||||
---------------|-----------|-----------
|
||||
<kbd>F1</kbd> | `SS3 P` | `ESC P`
|
||||
<kbd>F2</kbd> | `SS3 Q` | `ESC Q`
|
||||
<kbd>F3</kbd> | `SS3 R` | `ESC R`
|
||||
<kbd>F4</kbd> | `SS3 S` | `ESC S`
|
||||
|
||||
The function keys <kbd>F5</kbd> to <kbd>F12</kbd> generate the same sequences as they do in ANSI mode, except that they are not affected by modifiers. These correspond with a subset of the top-row functions keys on the VT220, along with the Windows <kbd>Menu</kbd> key mapping to the VT220 <kbd>DO</kbd> key.
|
||||
|
||||
Key | Sequence
|
||||
----------------|-------------
|
||||
<kbd>F5</kbd> | `CSI 1 5 ~`
|
||||
<kbd>F6</kbd> | `CSI 1 7 ~`
|
||||
<kbd>F7</kbd> | `CSI 1 8 ~`
|
||||
<kbd>F8</kbd> | `CSI 1 9 ~`
|
||||
<kbd>F9</kbd> | `CSI 2 0 ~`
|
||||
<kbd>F10</kbd> | `CSI 2 1 ~`
|
||||
<kbd>F11</kbd> | `CSI 2 3 ~`
|
||||
<kbd>F12</kbd> | `CSI 2 4 ~`
|
||||
<kbd>Menu</kbd> | `CSI 2 9 ~`
|
||||
|
||||
**Cursor and Editing Keys**
|
||||
|
||||
The cursor keys generate a simple ESC prefix instead of CSI or SS3. These correspond with the cursor keys on the VT100, except for <kbd>Home</kbd> and <kbd>End</kbd>, which are XTerm extensions. In V52 mode, they are not affected by modifiers, nor are they affected by the DECCKM _Cursor Keys_ mode.
|
||||
|
||||
Key | ANSI mode | VT52 mode
|
||||
-----------------|-----------|-----------
|
||||
<kbd>Up</kbd> | `CSI A` | `ESC A`
|
||||
<kbd>Down</kbd> | `CSI B` | `ESC B`
|
||||
<kbd>Right</kbd> | `CSI C` | `ESC C`
|
||||
<kbd>Left</kbd> | `CSI D` | `ESC D`
|
||||
<kbd>End</kbd> | `CSI F` | `ESC F`
|
||||
<kbd>Home</kbd> | `CSI H` | `ESC H`
|
||||
|
||||
The "editing" keys generate the same sequences as they do in ANSI mode, except that they are not affected by modifiers. These correspond with a subset of the editing keys on the VT220.
|
||||
|
||||
Key | Sequence
|
||||
----------------|-----------
|
||||
<kbd>Ins</kbd> | `CSI 2 ~`
|
||||
<kbd>Del</kbd> | `CSI 3 ~`
|
||||
<kbd>PgUp</kbd> | `CSI 5 ~`
|
||||
<kbd>PgDn</kbd> | `CSI 6 ~`
|
||||
|
||||
**Numeric Keypad**
|
||||
|
||||
With <kbd>Num Lock</kbd> disabled, most of the keys on the numeric keypad function the same as cursor keys or editing keys, but with the addition of a center <kbd>5</kbd> key. As a described above, the cursor keys generate a simple ESC prefix instead of CSI or SS3, while the editing keys remain unchanged (with the exception of modifiers).
|
||||
|
||||
In V52 mode, most modifiers are ignored, except for <kbd>Shift</kbd>, which is the equivalent of enabling <kbd>Num Lock</kbd> (i.e. the keys just generate their corresponding digit characters or `.`). With <kbd>Num Lock</kbd> enabled, it's the other way around - the digits are generated by default, while <kbd>Shift</kbd> enables the cursor/editing functionality.
|
||||
|
||||
Key | Alias | ANSI mode | VT52 mode
|
||||
-------------|-------|-----------|-----------
|
||||
<kbd>.</kbd> | Del | `CSI 3 ~` | `CSI 3 ~`
|
||||
<kbd>0</kbd> | Ins | `CSI 2 ~` | `CSI 2 ~`
|
||||
<kbd>1</kbd> | End | `CSI F` | `ESC F`
|
||||
<kbd>2</kbd> | Down | `CSI B` | `ESC B`
|
||||
<kbd>3</kbd> | PgDn | `CSI 6 ~` | `CSI 6 ~`
|
||||
<kbd>4</kbd> | Left | `CSI D` | `ESC D`
|
||||
<kbd>4</kbd> | Clear | `CSI E` | `ESC E`
|
||||
<kbd>6</kbd> | Right | `CSI C` | `ESC C`
|
||||
<kbd>7</kbd> | Home | `CSI H` | `ESC H`
|
||||
<kbd>8</kbd> | Up | `CSI A` | `ESC A`
|
||||
<kbd>9</kbd> | PgUp | `CSI 5 ~` | `CSI 5 ~`
|
||||
|
||||
When the DECKPAM _Alternate/Application Keypad Mode_ is set, though, the <kbd>Shift</kbd> modifier has a different affect on the numeric keypad. The sequences generated now correspond with the VT100/V52 numeric keypad keys. In VT52 mode, these sequences are not affected by any other modifiers, and this mode only applies when <kbd>Num Lock</kbd> is disabled.
|
||||
|
||||
Key | Alias | ANSI mode | VT52 mode
|
||||
-------------|-------|-----------|-----------
|
||||
<kbd>.</kbd> | Del | `SS3 2 n` | `ESC ? n`
|
||||
<kbd>0</kbd> | Ins | `SS3 2 p` | `ESC ? p`
|
||||
<kbd>1</kbd> | End | `SS3 2 q` | `ESC ? q `
|
||||
<kbd>2</kbd> | Down | `SS3 2 r` | `ESC ? r`
|
||||
<kbd>3</kbd> | PgDn | `SS3 2 s` | `ESC ? s`
|
||||
<kbd>4</kbd> | Left | `SS3 2 t` | `ESC ? t`
|
||||
<kbd>4</kbd> | Clear | `SS3 2 u` | `ESC ? u`
|
||||
<kbd>6</kbd> | Right | `SS3 2 v` | `ESC ? v`
|
||||
<kbd>7</kbd> | Home | `SS3 2 w` | `ESC ? w`
|
||||
<kbd>8</kbd> | Up | `SS3 2 x` | `ESC ? x`
|
||||
<kbd>9</kbd> | PgUp | `SS3 2 y` | `ESC ? y`
|
||||
|
||||
When the DECKPAM _Alternate/Application Keypad Mode_ is set, the "arithmetic" keys on the numeric keypad are also affected (this includes the <kbd>Enter</kbd> key). The sequences generated again correspond with the VT100/VT52 numeric keys (more or less), but this mapping is active even without the <kbd>Shift</kbd> modifier (and in VT52 mode all other modifiers are ignored too). As above, the mode only applies when <kbd>Num Lock</kbd> is disabled.
|
||||
|
||||
Key | ANSI mode | VT52 mode
|
||||
-----------------|-----------|-----------
|
||||
<kbd>*</kbd> | `SS3 j` | `ESC ? j`
|
||||
<kbd>+</kbd> | `SS3 k` | `ESC ? k`
|
||||
<kbd>-</kbd> | `SS3 m` | `ESC ? m`
|
||||
<kbd>/</kbd> | `SS3 o` | `ESC ? o`
|
||||
<kbd>Enter</kbd> | `SS3 M` | `ESC ? M`
|
||||
|
||||
Note that the DECKPAM _Application Keypad Mode_ is not currently implemented in ANSI mode, so perhaps that needs to be addressed first, before trying to add support for the VT52 _Alternate Keypad Mode_.
|
||||
|
||||
### Changing Modes
|
||||
|
||||
The `_PrivateModeParamsHelper` method in the `AdaptDispatch` class would need to be extended to handle the DECANM mode parameter, and trigger a function to switch to VT52 mode. The typical pattern for this seems to be through a `PrivateXXX` method in the `ConGetSet` interface. Then the `ConhostInternalGetSet` implementation can pass that flag on to the active output buffer's `StateMachine`, and the active input buffer's `TerminalInput` instance.
|
||||
|
||||
Changing back from VT52 mode to ANSI mode would need to be achieved with a separate VT52 command (`ESC <`), since the VT100 CSI mode sequences would no longer be active. This would be handled in the same place as the other VT52 commands, in the `OutputStateMachineEngine`, and then passed on to the mode selection method in the `AdaptDispatch` class described above (essentially the equivalent of the DECANM private mode being set).
|
||||
|
||||
### Additional VT52 Commands
|
||||
|
||||
Most of the missing VT52 functionality can be implemented in terms of existing VT100 methods.
|
||||
|
||||
* The _Cursor Up_ (`ESC A`), _Cursor Down_ (`ESC B`), _Cursor Left_ (`ESC D`), and _Cursor Right_ (`ESC C`) commands are already implemented.
|
||||
* The _Enter Graphics Mode_ (`ESC F`) and _Exit Graphics Mode_ (`ESC G`) commands can probably use the existing `DesignateCharset` method, although this would require a new `VTCharacterSets` option with a corresponding table of characters (see below).
|
||||
* The _Reverse Line Feed_ (`ESC I`) command can use the existing `ReverseLineFeed` method.
|
||||
* The _Erase to End of Display_ (`ESC J`) and _Erase to End of Line_ (`ESC K`) commands can use the existing `EraseInDisplay` and `EraseInLine` methods.
|
||||
* The _Cursor Home_ (`ESC H`) and _Direct Cursor Address_ (`ESC Y`) commands can probably be implemented using the `CursorPosition` method. Technically the _Direct Cursor Address_ has different rules for the boundary conditions (the CUP command clamps out of range coordinates, while the _Direct Cursor Address_ command ignores them, judged individually - one may be ignored while the other is interpreted). Nobody seems to get that right, though, so it's probably not that big a deal.
|
||||
* The _Identify_ (`ESC Z`) command may be the only one that doesn't build on existing functionality, but it should be a fairly trivial addition to the `AdaptDispatch` class. For a terminal emulating VT52, the identifying sequence should be `ESC / Z`.
|
||||
* The _Enter Keypad Mode_ (`ESC =`) and _Exit Keypad Mode_ (`ESC >`) commands can use the existing `SetKeypadMode` method, assuming the `TerminalInput` class already knows to generate different sequences when in VT52 mode (as described in the _Terminal Input_ section above).
|
||||
* The _Enter ANSI Mode_ (`ESC <`) command can just call through to the new mode selection method in the `AdaptDispatch` class as discussed in the _Changing Modes_ section above.
|
||||
|
||||
There are also a few VT52 print commands, but those are not technically part of the core command set, and considering we don't yet support any of the VT102 print commands, I think they can probably be considered out of scope for now. Briefly they are:
|
||||
|
||||
* _Auto Print_ on (`ESC ^`) and off (`ESC _`) commands. In auto print mode, a display line prints after you move the cursor off the line, or during an auto wrap.
|
||||
* _Print Controller_ on (`ESC W`) and off (`ESC X`) commands. When enabled, the terminal transmits received characters to the printer without displaying them.
|
||||
* The _Print Cursor Line_ (`ESC V`) command prints the display line with the cursor.
|
||||
* The _Print Screen_ (`ESC ]`) command prints the screen (or at least the scrolling region).
|
||||
|
||||
I suspect most, if not all of these, would be direct equivalents of the VT102 print commands, if we ever implemented those.
|
||||
|
||||
### Graphic Mode Character Set
|
||||
|
||||
The table below lists suggested mappings for the _Graphics Mode_ character set, based on the descriptions in the [VT102 User Guide](https://vt100.net/docs/vt102-ug/table5-15.html).
|
||||
|
||||
Note that there is only the one _fraction numerator_ character in Unicode, so superscript digits have instead been used for the numerators 3, 5, and 7. There are also not enough _horizontal scan line_ characters (for the _bar at scan x_ characters), so each of them is used twice to cover the full range.
|
||||
|
||||
ASCII Character |Mapped Glyph |Unicode Value |Spec Description
|
||||
----------------|---------------|---------------|----------------
|
||||
_ | |U+0020 |Blank
|
||||
` | |U+0020 |Reserved
|
||||
a |█ |U+2588 |Solid rectangle
|
||||
b |⅟ |U+215F |1/
|
||||
c |³ |U+00B3 |3/
|
||||
d |⁵ |U+2075 |5/
|
||||
e |⁷ |U+2077 |7/
|
||||
f |° |U+00B0 |Degrees
|
||||
g |± |U+00B1 |Plus or minus
|
||||
h |→ |U+2192 |Right arrow
|
||||
i |… |U+2026 |Ellipsis (dots)
|
||||
j |÷ |U+00F7 |Divide by
|
||||
k |↓ |U+2193 |Down arrow
|
||||
l |⎺ |U+23BA |Bar at scan 0
|
||||
m |⎺ |U+23BA |Bar at scan 1
|
||||
n |⎻ |U+23BB |Bar at scan 2
|
||||
o |⎻ |U+23BB |Bar at scan 3
|
||||
p |⎼ |U+23BC |Bar at scan 4
|
||||
q |⎼ |U+23BC |Bar at scan 5
|
||||
r |⎽ |U+23BD |Bar at scan 6
|
||||
s |⎽ |U+23BD |Bar at scan 7
|
||||
t |₀ |U+2080 |Subscript 0
|
||||
u |₁ |U+2081 |Subscript 1
|
||||
v |₂ |U+2082 |Subscript 2
|
||||
w |₃ |U+2083 |Subscript 3
|
||||
x |₄ |U+2084 |Subscript 4
|
||||
y |₅ |U+2085 |Subscript 5
|
||||
z |₆ |U+2086 |Subscript 6
|
||||
{ |₇ |U+2087 |Subscript 7
|
||||
\| |₈ |U+2088 |Subscript 8
|
||||
} |₉ |U+2089 |Subscript 9
|
||||
\~ |¶ |U+00B6 |Paragraph
|
||||
|
||||
### Testing
|
||||
|
||||
A simple unit test will need to be added to the `AdapterTest` class, to confirm that calls to toggle between the ANSI and VT52 modes in the `AdaptDispatch` class are correctly forwarded to the corresponding `PrivateXXX` handler in the `ConGetSet` interface.
|
||||
|
||||
The majority of the testing would be handled in the `StateMachineExternalTest` class though. These tests would confirm that the various VT52 sequences trigger the expected methods in the `ITermDispatch` interface when VT52 Mode is enabled, and also that they don't do anything when in ANSI mode.
|
||||
|
||||
There shouldn't really be any need for additional tests in the `ScreenBufferTests` class, since we're relying on existing VT100 functionality which should already be tested there.
|
||||
|
||||
For fuzzing support, we'll need to add the DECANM option to the `GeneratePrivateModeParamToken` method in the `VTCommandFuzzer` class, and also probably add two additional token generator methods - one specifically for the _Direct Cursor Address_ command, which requires parameters, and another to handle the remaining parameterless commands.
|
||||
|
||||
In terms of manual testing, it can be useful to run the _Test of VT52 mode_ option in Vttest, and confirm that everything looks correct there. It's also worth going through some of the options in the The _Test of keyboard_ section, since those tests aren't only intended for the later VT models - they do cover the VT52 keyboard as well.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
There is no additional UI associated with this feature.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
This should not impact accessibility any more than the existing escape sequences.
|
||||
|
||||
### Security
|
||||
|
||||
This should not introduce any new security issues.
|
||||
|
||||
### Reliability
|
||||
|
||||
This should not introduce any new reliability issues.
|
||||
|
||||
### Compatibility
|
||||
|
||||
This could be a breaking change for code that relies on the few existing VT52 commands being available without a mode change. However, that functionality is non-standard, and has not been around for that long. There is almost certainly more benefit in being able to implement the missing VT100 functionality than there is in retaining that non-standard behavior.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
The additional mode flags and associated processing in the `StateMachine` and `TerminalInput` classes could have some performance impact, but that is unlikely to be significant.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
The only negative impacts I can think of would be the potential for breaking changes, and the possible impact on performance, as discussed in the _Compatibility_ and _Performance_ sections above. But as with any new code, there is always the possibility of new bugs being introduced as well.
|
||||
|
||||
## Future considerations
|
||||
|
||||
As mentioned in the _Inspiration_ section, having the VT52 functionality isolated with a new mode would enable us to implement the VT100 Index (IND) escape sequence, which currently conflicts with the VT52 _Cursor Left_ command.
|
||||
|
||||
## Resources
|
||||
|
||||
* [VT52 Mode Control Sequences](https://vt100.net/docs/vt100-ug/chapter3.html#S3.3.5)
|
||||
* [VT100 ANSI/VT52 Mode (DECANM)](https://vt100.net/docs/vt100-ug/chapter3.html#DECANM)
|
||||
* [VT100 Index Sequence (IND)](https://vt100.net/docs/vt100-ug/chapter3.html#IND)
|
||||
* [VTTEST Test Utility](https://invisible-island.net/vttest/)
|
||||
* [DEC STD 070 Video Systems Reference Manual](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381)
|
||||
|
||||
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
---
|
||||
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)
|
||||