Compare commits

...

9 Commits

Author SHA1 Message Date
Dustin L. Howett
79e9f114d7 Migrate spelling-0.0.21 changes from main 2022-11-25 17:19:42 +01:00
Leonard Hecker
7f53e12060 wip 2022-11-25 17:19:42 +01:00
Leonard Hecker
8f346a7158 Rewrite Utf16Parser (#14417)
This commit replaces `Utf16Parser` with `<til/unicode.h>` which includes:
* `til::utf16_iterator` as a replacement for `Utf16Parser::Parse`
* `til::utf16_next` as a replacement for `Utf16Parser::ParseNext`

This fixes 2 bugs with `Utf16Parser`:
* Swallowing invalid surrogate pairs instead of turning them into U+FFFD.
* `std::vector<std::vector<wchar_t>>`. It's now >12000% faster.

## Validation Steps Performed
* New unit tests pass 
* Searching for narrow/wide characters in conhost works 
2022-11-23 21:13:36 +00:00
Junyoung Lee
437b5ac595 Add the setting "confirmCloseAllTabs" to SUI (#14419)
This commit adds the setting "confirmCloseAllTabs" to SUI.
The setting was added to the Interactions pane of Global Settings.

Closes #14413
Closes #14033
2022-11-21 22:03:56 +00:00
Ben Constable
feed768b3f [schema] Update allowed types for startingDirectory (#14408)
Update the schema to support null.

## PR Checklist
* [x] Closes #14299
* [x] Schema updated.
2022-11-21 16:02:24 -05:00
Mike Griese
937cadcad0 Spec for "matching" profiles in "New Tab Customization" (#12584)
Doc updated in response to some discussion in [#11326] and
[#7774]. In those PRs, it became clear that there needs to be a simple way of
collecting up a whole group of profiles automatically for sorting in these
menus. Although discussion centered on how hard it would be for extensions to
provide that customization themselves, the `match` statement was added as a way
to allow the user to easily filter those profiles themselves.

This was something we had originally considered as a "future consideration", but
ultimately deemed it to be out of scope for the initial spec review.

References:

* #1571
* #11326
* #7774
2022-11-18 14:13:15 -06:00
Dustin L. Howett
9aee510ce0 Add .git-blame-ignore-revs to make GitHub's blame view nicer (#14394)
Commits mentioned in this file will be acknowledged by GitHub, but
skipped in the blame view. Other tools use this as well.

You can make git use it by passing
`--ignore-revs-file .git-blame-ignore-revs` to `git blame`.

The only commits we're ignoring right now are codebase-wide reformatting
or line endings changes.
2022-11-15 18:49:57 -06:00
Dan Moseley
c9aeea1fdc Update bug template to help find the version number accurately (#14375)
Due to https://github.com/microsoft/terminal/issues/14335 using `wt -v` is not recommended. BTW, it might be nice if Ctrl-Shift-P and typing "about" or "version" gave the version, too.
2022-11-14 22:00:11 +00:00
Ian O'Neill
6b4b63b18a Ensure reading the buffer content actually returns the content (#14379)
## Summary of the Pull Request
Ensures that reading the buffer content actually returns the content.

## References
Regressed in #13626.

## PR Checklist
* [x] Closes #14378
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed

## Validation Steps Performed
Added a test.
2022-11-12 23:31:10 +00:00
47 changed files with 1361 additions and 1068 deletions

15
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,15 @@
# Commits mentioned in this file will automatically
# be skipped by GitHub's blame view.
# To use it with "git", run
# > git blame --ignore-revs-file ./.git-blame-ignore-revs
# Reformatted the entire codebase
9b92986b49bed8cc41fde4d6ef080921c41e6d9e
# Line Endings changes
cb7a76d96c92aa9fc7b03f69148fb0c75dff191d
5bbf61af8c8f12e6c05d07a696bf7d411b330a67
d07546a6fef73fa4e1fb1c2f01535843d1fcc212
# UTF-8 BOM changes
ddae2a1d49d604487d3c963e5eacbeb73861d986

View File

@@ -14,7 +14,7 @@ body:
label: Windows Terminal version
placeholder: "1.7.3651.0"
description: |
You can find the version in the about dialog, or by running `wt -v` at the commandline (for the Stable/Preview version youre reporting a bug about).
You can copy the version number from the About dialog. Open the About dialog by opening the menu with the "V" button (to the right of the "+" button that opens a new tab) and choosing About from the end of the list.
validations:
required: false

View File

@@ -6,6 +6,7 @@ File | Purpose | Format | Info
[reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject)
[excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
[patterns/*.txt](patterns/) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
[candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns)
[line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
[expect/*.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect)
[advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice)

View File

@@ -21,7 +21,7 @@ See the `README.md` in each directory for more information.
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
<details><summary>:clamp: If the flagged items are false positives</summary>
<details><summary>If the flagged items are :exploding_head: false positives</summary>
If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).

View File

@@ -1,7 +1,7 @@
admins
allcolors
apc
Apc
apc
breadcrumb
breadcrumbs
bsd
@@ -14,8 +14,8 @@ CMMI
copyable
cybersecurity
dalet
dcs
Dcs
dcs
dialytika
dje
downside
@@ -34,10 +34,12 @@ gantt
gcc
geeksforgeeks
ghe
github
gje
godbolt
hostname
hostnames
https
hyperlink
hyperlinking
hyperlinks
@@ -82,6 +84,7 @@ runtimes
shcha
slnt
Sos
ssh
timeline
timelines
timestamped
@@ -90,6 +93,7 @@ tokenizes
tonos
toolset
tshe
ubuntu
uiatextrange
UIs
und

View File

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

View File

@@ -2,14 +2,14 @@
(?:(?i)\.png$)
(?:^|/)(?i)COPYRIGHT
(?:^|/)(?i)LICEN[CS]E
(?:^|/)3rdparty/
(?:^|/)dirs$
(?:^|/)go\.mod$
(?:^|/)go\.sum$
(?:^|/)package(?:-lock|)\.json$
(?:^|/)sources(?:|\.dep)$
(?:^|/)vendor/
ignore$
SUMS$
\.a$
\.ai$
\.avi$
\.bmp$
@@ -20,6 +20,8 @@ SUMS$
\.crt$
\.csr$
\.dll$
\.docx?$
\.drawio$
\.DS_Store$
\.eot$
\.eps$
@@ -31,6 +33,7 @@ SUMS$
\.icns$
\.ico$
\.jar$
\.jks$
\.jpeg$
\.jpg$
\.key$
@@ -41,6 +44,7 @@ SUMS$
\.mod$
\.mp3$
\.mp4$
\.o$
\.ocf$
\.otf$
\.pbxproj$
@@ -48,22 +52,41 @@ SUMS$
\.pem$
\.png$
\.psd$
\.pyc$
\.runsettings$
\.s$
\.sig$
\.so$
\.svg$
\.svgz$
\.svgz?$
\.tar$
\.tgz$
\.tiff?$
\.ttf$
\.vsdx$
\.wav$
\.webm$
\.webp$
\.woff
\.woff2?$
\.xcf$
\.xls
\.xlsx?$
\.xpm$
\.yml$
\.zip$
^\.github/actions/spelling/
^\.github/fabricbot.json$
^\.gitignore$
^\Q.git-blame-ignore-revs\E$
^\Q.github/workflows/spelling.yml\E$
^\Qdoc/reference/windows-terminal-logo.ans\E$
^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$
^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$
^\Qsrc/host/ft_host/chafa.txt\E$
^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$
^\XamlStyler.json$
^build/config/
^consolegit2gitfilters\.json$
^dep/
@@ -90,12 +113,5 @@ SUMS$
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
^src/types/ut_types/UtilsTests.cpp$
^tools/ReleaseEngineering/ServicingPipeline.ps1$
^\.github/actions/spelling/
^\.github/fabricbot.json$
^\.gitignore$
^\Q.github/workflows/spelling.yml\E$
^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$
^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$
^\Qsrc/host/ft_host/chafa.txt\E$
^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$
^\XamlStyler.json$
ignore$
SUMS$

View File

@@ -5,26 +5,19 @@ AAAAAABBBBBBCCC
AAAAABBBBBBCCC
abcd
abcd
abcde
abcdef
ABCDEFG
ABCDEFGH
ABCDEFGHIJ
abcdefghijk
ABCDEFGHIJKLMNO
abcdefghijklmnop
ABCDEFGHIJKLMNOPQRST
abcdefghijklmnopqrstuvwxyz
ABCG
ABE
abf
BBBBB
BBBBBBBB
BBBBBBBBBBBBBBDDDD
BBBBBCCC
BBBBCCCCC
BBGGRR
CCE
EFG
EFGh
QQQQQQQQQQABCDEFGHIJ

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,3 @@
http
www
WCAG
winui
appshellintegration

View File

@@ -1,3 +1,11 @@
# reject `m_data` as there's a certain OS which has evil defines that break things if it's used elsewhere
# \bm_data\b
# If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test,
# you might not want to check in code where you were debugging w/ `fit()`, in which case, you might want
# to use this:
#\bfit\(
# s.b. GitHub
\bGithub\b
@@ -16,6 +24,12 @@
# s.b. greater than
\bgreater then\b
# s.b. into
#\sin to\s
# s.b. opt-in
\sopt in\s
# s.b. less than
\bless then\b
@@ -27,10 +41,22 @@
\b[Nn]o[nt][- ]existent\b
# s.b. preexisting
[Pp]re-existing
[Pp]re[- ]existing
# s.b. preempt
[Pp]re[- ]empt\b
# s.b. preemptively
[Pp]re-emptively
[Pp]re[- ]emptively
# s.b. reentrancy
[Rr]e[- ]entrancy
# s.b. reentrant
[Rr]e[- ]entrant
# s.b. workaround(s)
#\bwork[- ]arounds?\b
# Reject duplicate words
\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s

View File

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

View File

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

View File

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

View File

@@ -2396,7 +2396,10 @@
},
"startingDirectory": {
"description": "The directory the shell starts in when it is loaded.",
"type": "string"
"type": [
"string",
"null"
]
},
"suppressApplicationTitle": {
"description": "When set to true, tabTitle overrides the default title of the tab and any title change messages from the application will be suppressed. When set to false, tabTitle behaves as normal.",

View File

@@ -1,7 +1,7 @@
---
author: Mike Griese @zadjii-msft
created on: 2020-5-13
last updated: 2020-08-04
last updated: 2022-11-18
issue id: 1571
---
@@ -76,6 +76,34 @@ There are five `type`s of objects in this menu:
- The `"entries"` property specifies a list of menu entries that will appear
nested under this entry. This can contain other `"type":"folder"` groups as
well!
- The `"inline"` property accepts two values
- `auto`: When the folder only has one entry in it, don't actually create a
nested layer to then menu. Just place the single entry in the layer that
folder would occupy. (Useful for dynamic profile sources with only a
single entry).
- `never`: (**default**) Always create a nested entry, even for a single
sub-item.
- The `allowEmpty` property will force this entry to show up in the menu, even
if it doesn't have any profiles in it. This defaults to `false`, meaning
that folders without any entries in them will just be ignored when
generating the menu. This will be more useful with the `matchProfile` entry,
below.
When this is true, and the folder is empty, we should add a
placeholder `<empty>` entry to the menu, to indicate that no profiles were
in that folder.
- _This setting is probably pretty niche, and not a requirement_. More of a
theoretical suggestion than anything.
- In the case of no entries for this folder, we should make sure to also
reflect the `inline` property:
- `allowEmpty:true`, `inline:auto`: just ignore the entry at all. Don't
add a placeholder to the parent list.
- `allowEmpty:true`, `inline:never`: Add a nested entry, with an
`<empty>` placeholder.
- `allowEmpty:false`, `inline:auto`: just ignore the entry at all. Don't
add a placeholder to the parent list.
- `allowEmpty:false`, `inline:never`: just ignore the entry at all. Don't
add a placeholder to the parent list.
* `"type":"action"`: This represents a menu entry that should execute a specific
`ShortcutAction`.
- the `id` property will specify the global action ID (see [#6899], [#7175])
@@ -97,6 +125,16 @@ There are five `type`s of objects in this menu:
enabling all other profiles to also be accessible.
- The "name" of these entries will simply be the name of the profile
- The "icon" of these entries will simply be the profile's icon
- This won't include any profiles that have been included via `matchProfile`
entries (below)
* `"type": "matchProfile"`: Expands to all the profiles that match a given
string. This lets the user easily specify a whole collection of profiles for a
folder, without needing to add them all manually.
- `"name"`, `"commandline"` or `"source"`: These three properties are used to
filter the list of profiles, based on the matching property in the profile
itself. The value is a string to compare with the corresponding property in
the profile. A full string comparison is done - not a regex or partial
string match.
The "default" new tab menu could be imagined as the following blob of json:
@@ -108,6 +146,42 @@ The "default" new tab menu could be imagined as the following blob of json:
}
```
Alternatively, we could consider something like the following. This would place
CMD, PowerShell, and all PowerShell cores in the root at the top, followed by
nested entries for each subsequent dynamic profile generator.
```jsonc
{
"newTabMenu": [
{ "type":"profile", "profile": "cmd" },
{ "type":"profile", "profile": "Windows PowerShell" },
{ "type": "matchProfile", "source": "Microsoft.Terminal.PowerShellCore" }
{
"type": "folder",
"name": "WSL",
"entries": [ { "type": "matchProfile", "source": "Microsoft.Terminal.Wsl" } ]
},
{
"type": "folder",
"name": "Visual Studio",
"entries": [ { "type": "matchProfile", "source": "Microsoft.Terminal.VisualStudio" } ]
},
// ... etc for other profile generators
{ "type": "remainingProfiles" }
]
}
```
I might only recommend that for `userDefaults.json`, which is the json files
used as a template for a user's new settings file. This would prevent us from
moving the user's cheese too much, if they're already using the Terminal and
happy with their list as is. Especially consider someone who's default profile
is a WSL distro, which would now need two clicks to get to.
> _note_: We will also want to support the same `{ "key": "SomeResourceString"}`
> syntax used by the Command Palette commands
> for specifying localizable names, if we chose to pursue this route.
### Other considerations
Also considered during the investigation for this feature was re-using the list
@@ -154,6 +228,42 @@ The design chosen in this spec more cleanly separates the responsibilities of
the list of profiles and the contents of the new tab menu. This way, each object
can be defined independent of the structure of the other.
Regarding implementation of `matchProfile` entries: In order to build the menu,
we'll evaluate the entries in the following order:
* all explicit `profile` entries
* then all `matchProfile` entries, using profiles not already specified
* then expand out `remainingProfiles` with anything not found above.
As an example:
```jsonc
{
"newTabMenu": [
{ "type": "matchProfile", "source": "Microsoft.Terminal.Wsl" }
{
"type": "folder",
"name": "WSLs",
"entries": [ { "type": "matchProfile", "source": "Microsoft.Terminal.Wsl" } ]
},
{ "type": "remainingProfiles" }
]
}
```
For profiles { "Profile A", "Profile B (WSL)", "Profile C (WSL)" }, This would
expand to:
```
New Tab Button ▽
├─ Profile A
├─ Profile B (WSL)
├─ Profile C (WSL)
└─ WSLs
└─ Profile B (WSL)
└─ Profile C (WSL)
```
## UI/UX Design
See the above _figure 1_.
@@ -289,7 +399,39 @@ And assuming the user has bound:
- Close Tab: `{ "action": "closeTab", "index": "${selectedTab.index}" }`
- Close Other Tabs: `{ "action": "closeTabs", "otherThan": "${selectedTab.index}" }`
- Close Tabs to the Right: `{ "action": "closeTabs", "after": "${selectedTab.index}" }`
* We may want to consider regex, tag-based, or some other type of matching for
`matchProfile` entries in the future. We originally considered using regex for
`matchProfile` by default, but decided instead on full string matches to leave
room for regex matching in the future. Should we chose to pursue something
like that, we should use a settings structure like:
```json
"type": "profileMatch",
"source": { "type": "regex", "value": ".*wsl.*" }
```
* We may want to expand `matchProfile` to match on other properties too. (`title`?)
* We may want to consider adding support for capture groups, e.g.
```json
{
"type": "profileMatch",
"name": { "type": "regex", "value": "^ssh: (.*)" }
}
```
for matching to all your `ssh: ` profiles, but populate the name in the entry
with that first capture group. So, ["ssh: foo", "ssh: bar"] would just expand
to a "foo" and "bar" entry.
## Updates
_February 2022_: Doc updated in response to some discussion in [#11326] and
[#7774]. In those PRs, it became clear that there needs to be a simple way of
collecting up a whole group of profiles automatically for sorting in these
menus. Although discussion centered on how hard it would be for extensions to
provide that customization themselves, the `match` statement was added as a way
to allow the user to easily filter those profiles themselves.
This was something we had originally considered as a "future consideration", but
ultimately deemed it to be out of scope for the initial spec review.
<!-- Footnotes -->
[#2046]: https://github.com/microsoft/terminal/issues/2046
@@ -298,3 +440,5 @@ And assuming the user has bound:
[#3337]: https://github.com/microsoft/terminal/issues/3337
[#6899]: https://github.com/microsoft/terminal/issues/6899
[#7175]: https://github.com/microsoft/terminal/issues/7175
[#11326]: https://github.com/microsoft/terminal/issues/11326
[#7774]: https://github.com/microsoft/terminal/issues/7774

View File

@@ -5,8 +5,9 @@
#include "OutputCellIterator.hpp"
#include <til/unicode.h>
#include "../../types/inc/convert.hpp"
#include "../../types/inc/Utf16Parser.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../inc/conattrs.hpp"
@@ -392,7 +393,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
const TextAttribute attr,
const TextAttributeBehavior behavior)
{
const auto glyph = Utf16Parser::ParseNext(view);
const auto glyph = til::utf16_next(view);
const auto dbcsAttr = IsGlyphFullWidth(glyph) ? DbcsAttribute::Leading : DbcsAttribute::Single;
return OutputCellView(glyph, dbcsAttr, attr, behavior);
}

View File

@@ -5,8 +5,9 @@
#include "search.h"
#include <til/unicode.h>
#include "textBuffer.hpp"
#include "../types/inc/Utf16Parser.hpp"
#include "../types/inc/GlyphWidth.hpp"
using namespace Microsoft::Console::Types;
@@ -192,12 +193,11 @@ bool Search::_FindNeedleInHaystackAt(const til::point pos, til::point& start, ti
auto bufferPos = pos;
for (const auto& needleCell : _needle)
for (const auto& needleChars : _needle)
{
// Haystack is the buffer. Needle is the string we were given.
const auto hayIter = _uiaData.GetTextBuffer().GetTextDataAt(bufferPos);
const auto hayChars = *hayIter;
const auto needleChars = std::wstring_view(needleCell.data(), needleCell.size());
// If we didn't match at any point of the needle, return false.
if (!_CompareChars(hayChars, needleChars))
@@ -328,13 +328,12 @@ void Search::_UpdateNextPosition()
// - wstr - String that will be our search term
// Return Value:
// - Structured text data for comparison to screen buffer text data.
std::vector<std::vector<wchar_t>> Search::s_CreateNeedleFromString(const std::wstring_view wstr)
std::vector<std::wstring> Search::s_CreateNeedleFromString(const std::wstring_view wstr)
{
const auto charData = Utf16Parser::Parse(wstr);
std::vector<std::vector<wchar_t>> cells;
for (const auto chars : charData)
std::vector<std::wstring> cells;
for (const auto& chars : til::utf16_iterator{ wstr })
{
if (IsGlyphFullWidth(std::wstring_view{ chars.data(), chars.size() }))
if (IsGlyphFullWidth(chars))
{
cells.emplace_back(chars);
}

View File

@@ -68,7 +68,7 @@ private:
static til::point s_GetInitialAnchor(const Microsoft::Console::Types::IUiaData& uiaData, const Direction dir);
static std::vector<std::vector<wchar_t>> s_CreateNeedleFromString(const std::wstring_view wstr);
static std::vector<std::wstring> s_CreateNeedleFromString(const std::wstring_view wstr);
bool _reachedEnd = false;
til::point _coordNext;
@@ -76,7 +76,7 @@ private:
til::point _coordSelEnd;
const til::point _coordAnchor;
const std::vector<std::vector<wchar_t>> _needle;
const std::vector<std::wstring> _needle;
const Direction _direction;
const Sensitivity _sensitivity;
Microsoft::Console::Types::IUiaData& _uiaData;

View File

@@ -6,11 +6,11 @@
#include "textBuffer.hpp"
#include <til/hash.h>
#include <til/unicode.h>
#include "../renderer/base/renderer.hpp"
#include "../types/inc/utils.hpp"
#include "../types/inc/convert.hpp"
#include "../../types/inc/Utf16Parser.hpp"
#include "../../types/inc/GlyphWidth.hpp"
namespace
@@ -2810,16 +2810,14 @@ PointTree TextBuffer::GetPatterns(const til::CoordType firstRow, const til::Coor
// match and the previous match, so we use the size of the prefix
// along with the size of the match to determine the locations
til::CoordType prefixSize = 0;
for (const auto parsedGlyph : Utf16Parser::Parse(i->prefix().str()))
for (const auto str = i->prefix().str(); const auto& glyph : til::utf16_iterator{ str })
{
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
prefixSize += IsGlyphFullWidth(glyph) ? 2 : 1;
}
const auto start = lenUpToThis + prefixSize;
til::CoordType matchSize = 0;
for (const auto parsedGlyph : Utf16Parser::Parse(i->str()))
for (const auto str = i->str(); const auto& glyph : til::utf16_iterator{ str })
{
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
matchSize += IsGlyphFullWidth(glyph) ? 2 : 1;
}
const auto end = start + matchSize;

View File

@@ -7,7 +7,6 @@
#include "../textBuffer.hpp"
#include "../../renderer/inc/DummyRenderer.hpp"
#include "../../types/inc/Utf16Parser.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include <IDataSource.h>

View File

@@ -10,7 +10,6 @@
#include <DefaultSettings.h>
#include <unicode.hpp>
#include <Utf16Parser.hpp>
#include <WinUser.h>
#include <LibraryResources.h>
@@ -1744,7 +1743,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto strEnd = rowText.find_last_not_of(UNICODE_SPACE);
if (strEnd != decltype(rowText)::npos)
{
str.append(rowText.substr(strEnd + 1));
str.append(rowText.substr(0, strEnd + 1));
}
if (!row.WasWrapForced())

View File

@@ -5,7 +5,6 @@
#include "ControlInteractivity.h"
#include <DefaultSettings.h>
#include <unicode.hpp>
#include <Utf16Parser.hpp>
#include <Utils.h>
#include <LibraryResources.h>
#include "../../types/inc/GlyphWidth.hpp"

View File

@@ -5,7 +5,6 @@
#include "TermControl.h"
#include <unicode.hpp>
#include <Utf16Parser.hpp>
#include <LibraryResources.h>
#include "TermControlAutomationPeer.h"

View File

@@ -88,6 +88,11 @@
<ToggleSwitch IsOn="{x:Bind ViewModel.DetectURLs, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<local:SettingContainer x:Uid="Globals_ConfirmCloseAllTabs">
<ToggleSwitch IsOn="{x:Bind ViewModel.ConfirmCloseAllTabs, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
</StackPanel>
</ScrollViewer>
</Page>

View File

@@ -27,6 +27,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, FocusFollowMouse);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, DetectURLs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WordDelimiters);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ConfirmCloseAllTabs);
private:
Model::GlobalAppSettings _GlobalSettings;

View File

@@ -24,5 +24,6 @@ namespace Microsoft.Terminal.Settings.Editor
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, FocusFollowMouse);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, DetectURLs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(String, WordDelimiters);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ConfirmCloseAllTabs);
}
}

View File

@@ -1557,4 +1557,8 @@
<value>Set as default</value>
<comment>Text label for a button that, when invoked, sets the selected color scheme as the default scheme to use.</comment>
</data>
<data name="Globals_ConfirmCloseAllTabs.Header" xml:space="preserve">
<value>Confirm before closing all tabs</value>
<comment>Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open.</comment>
</data>
</root>

View File

@@ -28,6 +28,7 @@
#include "DefaultTerminal.h"
#include "FileUtils.h"
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::ApplicationModel::AppExtensions;
using namespace winrt::Microsoft::Terminal::Settings;
@@ -912,6 +913,65 @@ try
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
const auto _ = [&]() -> winrt::fire_and_forget {
co_await winrt::resume_background();
co_await winrt::resume_after(std::chrono::seconds(2));
{
const auto folder = winrt::Windows::Storage::ApplicationData::Current().LocalFolder();
auto file = co_await folder.GetFileAsync(SettingsFilename);
auto props = co_await file.GetBasicPropertiesAsync();
const auto beg = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < 1000; ++i)
{
file = co_await folder.GetFileAsync(SettingsFilename);
props = co_await file.GetBasicPropertiesAsync();
}
const auto end = std::chrono::high_resolution_clock::now();
const auto dur = std::chrono::duration<double>(end - beg).count();
auto out = std::to_wstring(dur);
out += L"ms\n";
OutputDebugStringW(out.c_str());
}
{
const auto path = _settingsPath();
FILETIME ftCreate, ftAccess, ftWrite;
wil::unique_hfile file{ CreateFileW(path.c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr) };
THROW_LAST_ERROR_IF(!file);
THROW_LAST_ERROR_IF(!GetFileTime(file.get(), &ftCreate, &ftAccess, &ftWrite));
const auto beg = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < 1000; ++i)
{
file = wil::unique_hfile{ CreateFileW(path.c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr) };
THROW_LAST_ERROR_IF(!file);
THROW_LAST_ERROR_IF(!GetFileTime(file.get(), &ftCreate, &ftAccess, &ftWrite));
}
const auto end = std::chrono::high_resolution_clock::now();
const auto dur = std::chrono::duration<double>(end - beg).count();
auto out = std::to_wstring(dur);
out += L"ms\n";
OutputDebugStringW(out.c_str());
}
}();
return *settings;
}
catch (const SettingsException& ex)

View File

@@ -36,6 +36,7 @@ namespace ControlUnitTests
TEST_METHOD(TestClearScrollback);
TEST_METHOD(TestClearScreen);
TEST_METHOD(TestClearAll);
TEST_METHOD(TestReadEntireBuffer);
TEST_CLASS_SETUP(ModuleSetup)
{
@@ -340,4 +341,22 @@ namespace ControlUnitTests
// The ConptyRoundtripTests test the actual clearing of the contents.
}
void ControlCoreTests::TestReadEntireBuffer()
{
auto [settings, conn] = _createSettingsAndConnection();
Log::Comment(L"Create ControlCore object");
auto core = createCore(*settings, *conn);
VERIFY_IS_NOT_NULL(core);
_standardInit(core);
Log::Comment(L"Print some text");
conn->WriteInput(L"This is some text \r\n");
conn->WriteInput(L"with varying amounts \r\n");
conn->WriteInput(L"of whitespace \r\n");
Log::Comment(L"Check the buffer contents");
VERIFY_ARE_EQUAL(L"This is some text\r\nwith varying amounts\r\nof whitespace\r\n",
core->ReadEntireBuffer());
}
}

View File

@@ -12,7 +12,6 @@
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/Viewport.hpp"
#include "../types/inc/convert.hpp"
#include "../types/inc/Utf16Parser.hpp"
#include <algorithm>
#include <iterator>

View File

@@ -4,14 +4,14 @@
#include "precomp.h"
#include "conimeinfo.h"
#include "conareainfo.h"
#include <til/unicode.h>
#include "conareainfo.h"
#include "_output.h"
#include "dbcs.h"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/GlyphWidth.hpp"
#include "../types/inc/Utf16Parser.hpp"
// Attributes flags:
#define COMMON_LVB_GRID_SINGLEFLAG 0x2000 // DBCS: Grid attribute: use for ime cursor.
@@ -223,12 +223,9 @@ std::vector<OutputCell> ConsoleImeInfo::s_ConvertToCells(const std::wstring_view
{
std::vector<OutputCell> cells;
// - Convert incoming wchar_t stream into UTF-16 units.
const auto glyphs = Utf16Parser::Parse(text);
// - Walk through all of the grouped up text, match up the correct attribute to it, and make a new cell.
size_t attributesUsed = 0;
for (const auto& parsedGlyph : glyphs)
for (const auto& parsedGlyph : til::utf16_iterator{ text })
{
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
// Collect up attributes that apply to this glyph range.

View File

@@ -34,7 +34,6 @@
<ClCompile Include="TitleTests.cpp" />
<ClCompile Include="UtilsTests.cpp" />
<ClCompile Include="Utf8ToWideCharParserTests.cpp" />
<ClCompile Include="Utf16ParserTests.cpp" />
<ClCompile Include="InputBufferTests.cpp" />
<ClCompile Include="ReadWaitTests.cpp" />
<ClCompile Include="ViewportTests.cpp" />

View File

@@ -72,9 +72,6 @@
<ClCompile Include="AliasTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utf16ParserTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SearchTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>

View File

@@ -1,211 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
#include "../../inc/consoletaeftemplates.hpp"
#include "../../types/inc/Utf16Parser.hpp"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
static const std::vector<wchar_t> CyrillicChar = { 0x0431 }; // lowercase be
static const std::vector<wchar_t> LatinChar = { 0x0061 }; // uppercase A
static const std::vector<wchar_t> FullWidthChar = { 0xFF2D }; // fullwidth latin small letter m
static const std::vector<wchar_t> GaelicChar = { 0x1E41 }; // latin small letter m with dot above
static const std::vector<wchar_t> HiraganaChar = { 0x3059 }; // hiragana su
static const std::vector<wchar_t> SunglassesEmoji = { 0xD83D, 0xDE0E }; // smiling face with sunglasses emoji
class Utf16ParserTests
{
TEST_CLASS(Utf16ParserTests);
TEST_METHOD(CanParseNonSurrogateText)
{
const std::vector<std::vector<wchar_t>> expected = { CyrillicChar, LatinChar, FullWidthChar, GaelicChar, HiraganaChar };
std::wstring wstr;
for (const auto& charData : expected)
{
wstr.push_back(charData.at(0));
}
const auto result = Utf16Parser::Parse(wstr);
VERIFY_ARE_EQUAL(expected.size(), result.size());
for (size_t i = 0; i < result.size(); ++i)
{
const auto& sequence = result.at(i);
VERIFY_ARE_EQUAL(sequence, expected.at(i));
}
}
TEST_METHOD(CanParseSurrogatePairs)
{
const std::wstring wstr{ SunglassesEmoji.begin(), SunglassesEmoji.end() };
const auto result = Utf16Parser::Parse(wstr);
VERIFY_ARE_EQUAL(result.size(), 1u);
VERIFY_ARE_EQUAL(result.at(0).size(), SunglassesEmoji.size());
for (size_t i = 0; i < SunglassesEmoji.size(); ++i)
{
VERIFY_ARE_EQUAL(result.at(0).at(i), SunglassesEmoji.at(i));
}
}
TEST_METHOD(WillDropBadSurrogateCombinations)
{
// test dropping of invalid leading surrogates
std::wstring wstr{ SunglassesEmoji.begin(), SunglassesEmoji.end() };
wstr += wstr;
wstr.at(1) = SunglassesEmoji.at(0); // wstr contains 3 leading, 1 trailing surrogate sequence
auto result = Utf16Parser::Parse(wstr);
VERIFY_ARE_EQUAL(result.size(), 1u);
VERIFY_ARE_EQUAL(result.at(0).size(), SunglassesEmoji.size());
for (size_t i = 0; i < SunglassesEmoji.size(); ++i)
{
VERIFY_ARE_EQUAL(result.at(0).at(i), SunglassesEmoji.at(i));
}
// test dropping of invalid trailing surrogates
wstr = { SunglassesEmoji.begin(), SunglassesEmoji.end() };
wstr += wstr;
wstr.at(0) = SunglassesEmoji.at(1); // wstr contains 2 trailing, 1 leading, 1 trailing surrogate sequence
result = Utf16Parser::Parse(wstr);
VERIFY_ARE_EQUAL(result.size(), 1u);
VERIFY_ARE_EQUAL(result.at(0).size(), SunglassesEmoji.size());
for (size_t i = 0; i < SunglassesEmoji.size(); ++i)
{
VERIFY_ARE_EQUAL(result.at(0).at(i), SunglassesEmoji.at(i));
}
}
const std::wstring_view Replacement{ &UNICODE_REPLACEMENT, 1 };
TEST_METHOD(ParseNextLeadOnly)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
const auto expected = Replacement;
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextTrailOnly)
{
std::wstring wstr{ SunglassesEmoji.at(1) };
const auto expected = Replacement;
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextSingleOnly)
{
std::wstring wstr{ CyrillicChar.at(0) };
const auto expected = std::wstring_view{ CyrillicChar.data(), CyrillicChar.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextLeadLead)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
wstr += SunglassesEmoji.at(0);
const auto expected = Replacement;
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextLeadTrail)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
wstr += SunglassesEmoji.at(1);
const auto expected = std::wstring_view{ SunglassesEmoji.data(), SunglassesEmoji.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextTrailTrail)
{
std::wstring wstr{ SunglassesEmoji.at(1) };
wstr += SunglassesEmoji.at(1);
const auto expected = Replacement;
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextLeadSingle)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
wstr += LatinChar.at(0);
const auto expected = std::wstring_view{ LatinChar.data(), LatinChar.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextTrailSingle)
{
std::wstring wstr{ SunglassesEmoji.at(1) };
wstr += LatinChar.at(0);
const auto expected = std::wstring_view{ LatinChar.data(), LatinChar.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextLeadLeadTrail)
{
std::wstring wstr{ SunglassesEmoji.at(0) };
wstr += SunglassesEmoji.at(0);
wstr += SunglassesEmoji.at(1);
const auto expected = std::wstring_view{ SunglassesEmoji.data(), SunglassesEmoji.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextTrailLeadTrail)
{
std::wstring wstr{ SunglassesEmoji.at(1) };
wstr += SunglassesEmoji.at(0);
wstr += SunglassesEmoji.at(1);
const auto expected = std::wstring_view{ SunglassesEmoji.data(), SunglassesEmoji.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(ParseNextSingleLeadTrail)
{
std::wstring wstr{ GaelicChar.at(0) };
wstr += SunglassesEmoji.at(0);
wstr += SunglassesEmoji.at(1);
const auto expected = std::wstring_view{ GaelicChar.data(), GaelicChar.size() };
const auto actual = Utf16Parser::ParseNext(wstr);
VERIFY_ARE_EQUAL(expected, actual);
}
};

View File

@@ -28,7 +28,6 @@ SOURCES = \
ClipboardTests.cpp \
SelectionTests.cpp \
Utf8ToWideCharParserTests.cpp \
Utf16ParserTests.cpp \
OutputCellIteratorTests.cpp \
InitTests.cpp \
TitleTests.cpp \

164
src/inc/til/unicode.h Normal file
View File

@@ -0,0 +1,164 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
namespace til
{
namespace details
{
inline constexpr wchar_t UNICODE_REPLACEMENT = 0xFFFD;
}
static constexpr bool is_surrogate(const wchar_t wch) noexcept
{
return (wch & 0xF800) == 0xD800;
}
static constexpr bool is_leading_surrogate(const wchar_t wch) noexcept
{
return (wch & 0xFC00) == 0xD800;
}
static constexpr bool is_trailing_surrogate(const wchar_t wch) noexcept
{
return (wch & 0xFC00) == 0xDC00;
}
// Verifies the beginning of the given UTF16 string and returns the first UTF16 sequence
// or U+FFFD otherwise. It's not really useful and at the time of writing only a
// single caller uses this. It's best to delete this if you read this comment.
constexpr std::wstring_view utf16_next(std::wstring_view wstr) noexcept
{
auto it = wstr.begin();
const auto end = wstr.end();
auto ptr = &details::UNICODE_REPLACEMENT;
size_t len = 1;
if (it != end)
{
const auto wch = *it;
ptr = &*it;
if (is_surrogate(wch))
{
++it;
const auto wch2 = it != end ? *it : wchar_t{};
if (is_leading_surrogate(wch) && is_trailing_surrogate(wch2))
{
len = 2;
++it;
}
else
{
ptr = &details::UNICODE_REPLACEMENT;
}
}
}
return { ptr, len };
}
// Splits a UTF16 string into codepoints, yielding `wstring_view`s of UTF16 text. Use it as:
// for (const auto& str : til::utf16_iterator{ input }) { ... }
struct utf16_iterator
{
struct sentinel
{
};
struct iterator
{
using iterator_category = std::forward_iterator_tag;
using value_type = std::wstring_view;
using reference = value_type&;
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
explicit constexpr iterator(utf16_iterator& p) noexcept :
_iter{ p }
{
}
const value_type& operator*() const noexcept
{
return _iter.value();
}
iterator& operator++() noexcept
{
_iter._advance = true;
return *this;
}
bool operator!=(const sentinel&) const noexcept
{
return _iter.valid();
}
private:
utf16_iterator& _iter;
};
explicit constexpr utf16_iterator(std::wstring_view wstr) noexcept :
_it{ wstr.begin() }, _end{ wstr.end() }, _advance{ _it != _end }
{
}
iterator begin() noexcept
{
return iterator{ *this };
}
sentinel end() noexcept
{
return sentinel{};
}
private:
bool valid() const noexcept
{
return _it != _end;
}
void advance() noexcept
{
const auto wch = *_it;
auto ptr = &*_it;
size_t len = 1;
++_it;
if (is_surrogate(wch))
{
const auto wch2 = _it != _end ? *_it : wchar_t{};
if (is_leading_surrogate(wch) && is_trailing_surrogate(wch2))
{
len = 2;
++_it;
}
else
{
ptr = &details::UNICODE_REPLACEMENT;
}
}
_value = { ptr, len };
_advance = false;
}
const std::wstring_view& value() noexcept
{
if (_advance)
{
advance();
}
return _value;
}
std::wstring_view::iterator _it;
std::wstring_view::iterator _end;
std::wstring_view _value;
bool _advance = true;
};
}

View File

@@ -2,17 +2,16 @@
// Licensed under the MIT license.
#include "precomp.h"
#include <windows.h>
#include "terminalInput.hpp"
#include "strsafe.h"
#include <til/unicode.h>
#include <strsafe.h>
#define WIL_SUPPORT_BITOPERATION_PASCAL_NAMES
#include <wil/Common.h>
#include "../../interactivity/inc/VtApiRedirection.hpp"
#include "../../inc/unicode.hpp"
#include "../../types/inc/Utf16Parser.hpp"
using namespace Microsoft::Console::VirtualTerminal;
@@ -739,7 +738,7 @@ bool TerminalInput::HandleFocus(const bool focused) noexcept
// - ch: The UTF-16 character to send.
void TerminalInput::_SendChar(const wchar_t ch)
{
if (Utf16Parser::IsLeadingSurrogate(ch))
if (til::is_leading_surrogate(ch))
{
if (_leadingSurrogate.has_value())
{

View File

@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
#include <til/unicode.h>
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
#define REPLACEMENT L"\xFFFD"
#define LEADING L"\xD801"
#define TRAILING L"\xDC01"
#define PAIR L"\xD801\xDC01"
class UnicodeTests
{
TEST_CLASS(UnicodeTests);
TEST_METHOD(utf16_next)
{
struct Test
{
std::wstring_view input;
std::wstring_view expected;
};
static constexpr std::array tests{
Test{ L"", REPLACEMENT },
Test{ L"a", L"a" },
Test{ L"abc", L"a" },
Test{ L"a" PAIR, L"a" },
Test{ L"a" LEADING, L"a" },
Test{ L"a" TRAILING, L"a" },
Test{ PAIR L"a", PAIR },
Test{ LEADING L"a", REPLACEMENT },
Test{ TRAILING L"a", REPLACEMENT },
};
for (const auto& t : tests)
{
const auto actual = til::utf16_next(t.input);
VERIFY_ARE_EQUAL(t.expected, actual);
}
}
TEST_METHOD(utf16_iterator)
{
struct Test
{
std::wstring_view input;
til::some<std::wstring_view, 5> expected;
};
static constexpr std::array tests{
Test{ L"", {} },
Test{ L"a", { L"a" } },
Test{ L"abc", { L"a", L"b", L"c" } },
Test{ PAIR L"a" PAIR L"b" PAIR, { PAIR, L"a", PAIR, L"b", PAIR } },
Test{ LEADING L"a" LEADING L"b" LEADING, { REPLACEMENT, L"a", REPLACEMENT, L"b", REPLACEMENT } },
Test{ TRAILING L"a" TRAILING L"b" TRAILING, { REPLACEMENT, L"a", REPLACEMENT, L"b", REPLACEMENT } },
Test{ L"a" TRAILING LEADING L"b", { L"a", REPLACEMENT, REPLACEMENT, L"b" } },
};
for (const auto& t : tests)
{
auto it = t.expected.begin();
const auto end = t.expected.end();
for (const auto& v : til::utf16_iterator{ t.input })
{
VERIFY_ARE_NOT_EQUAL(end, it);
VERIFY_ARE_EQUAL(*it, v);
++it;
}
VERIFY_ARE_EQUAL(end, it);
}
}
};

View File

@@ -33,6 +33,7 @@ SOURCES = \
StaticMapTests.cpp \
string.cpp \
u8u16convertTests.cpp \
UnicodeTests.cpp \
DefaultResource.rc \
# These tests are disabled because of a missing symbol.

View File

@@ -35,6 +35,7 @@
<ClCompile Include="string.cpp" />
<ClCompile Include="throttled_func.cpp" />
<ClCompile Include="u8u16convertTests.cpp" />
<ClCompile Include="UnicodeTests.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\inc\til\at.h" />
@@ -64,6 +65,7 @@
<ClInclude Include="..\..\inc\til\throttled_func.h" />
<ClInclude Include="..\..\inc\til\ticket_lock.h" />
<ClInclude Include="..\..\inc\til\u8u16convert.h" />
<ClInclude Include="..\..\inc\til\unicode.h" />
<ClInclude Include="..\precomp.h" />
</ItemGroup>
<ItemDefinitionGroup>

View File

@@ -26,6 +26,7 @@
<ClCompile Include="string.cpp" />
<ClCompile Include="throttled_func.cpp" />
<ClCompile Include="u8u16convertTests.cpp" />
<ClCompile Include="UnicodeTests.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\precomp.h" />
@@ -110,6 +111,9 @@
<ClInclude Include="..\..\inc\til\u8u16convert.h">
<Filter>inc</Filter>
</ClInclude>
<ClInclude Include="..\..\inc\til\unicode.h">
<Filter>inc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="inc">

View File

@@ -1,91 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "inc/Utf16Parser.hpp"
#include "unicode.hpp"
// Routine Description:
// - Finds the next single collection for the codepoint out of the given UTF-16 string information.
// - In simpler terms, it will group UTF-16 surrogate pairs into a single unit or give you a valid single-item UTF-16 character.
// - Does not validate UTF-16 input beyond proper leading/trailing character sequences.
// Arguments:
// - wstr - The UTF-16 string to parse.
// Return Value:
// - A view into the string given of just the next codepoint unit.
std::wstring_view Utf16Parser::ParseNext(std::wstring_view wstr) noexcept
{
for (size_t pos = 0; pos < wstr.size(); ++pos)
{
const auto wch = wstr.at(pos);
// If it's a lead and followed directly by a trail, then return the pair.
// If it's not followed directly by the trail, go around again and seek forward.
if (IsLeadingSurrogate(wch))
{
// Try to find the next item... if it isn't there, we'll go around again.
const auto posNext = pos + 1;
if (posNext < wstr.size())
{
// If we found it and it's trailing, return the pair.
const auto wchNext = wstr.at(posNext);
if (IsTrailingSurrogate(wchNext))
{
return wstr.substr(pos, 2);
}
}
// If we missed either if in any way, we'll fall through and go around again searching for more.
}
// If it's just a trail at this point, go around again and seek forward.
else if (IsTrailingSurrogate(wch))
{
continue;
}
// If it's neither lead nor trail, then it's < U+10000 and it can be returned as a single wchar_t point.
else
{
return wstr.substr(pos, 1);
}
}
// If we get all the way through and there's nothing valid, then this is just a replacement character as it was broken/garbage.
return std::wstring_view{ &UNICODE_REPLACEMENT, 1 };
}
// Routine Description:
// - formats a utf16 encoded wstring and splits the codepoints into individual collections.
// - will drop badly formatted leading/trailing char sequences.
// - does not validate utf16 input beyond proper leading/trailing char sequences.
// Arguments:
// - wstr - the string to parse
// Return Value:
// - a vector of utf16 codepoints. glyphs that require surrogate pairs will be grouped
// together in a vector and codepoints that use only one wchar will be in a vector by themselves.
std::vector<std::vector<wchar_t>> Utf16Parser::Parse(std::wstring_view wstr)
{
std::vector<std::vector<wchar_t>> result;
std::vector<wchar_t> sequence;
for (const auto wch : wstr)
{
if (IsLeadingSurrogate(wch))
{
sequence.clear();
sequence.push_back(wch);
}
else if (IsTrailingSurrogate(wch))
{
if (!sequence.empty())
{
sequence.push_back(wch);
result.push_back(sequence);
sequence.clear();
}
}
else
{
result.push_back({ wch });
}
}
return result;
}

View File

@@ -1,46 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Module Name:
- Utf16Parser.hpp
Abstract:
- Parser for grouping together utf16 codepoints from a string of utf16 encoded text
Author(s):
- Austin Diviness (AustDi) 25-Apr-2018
--*/
#pragma once
#include <vector>
class Utf16Parser final
{
public:
static std::vector<std::vector<wchar_t>> Parse(std::wstring_view wstr);
static std::wstring_view ParseNext(std::wstring_view wstr) noexcept;
// Routine Description:
// - checks if wchar is a utf16 leading surrogate
// Arguments:
// - wch - the wchar to check
// Return Value:
// - true if wch is a leading surrogate, false otherwise
static constexpr bool IsLeadingSurrogate(const wchar_t wch) noexcept
{
return wch >= 0xD800 && wch <= 0xDBFF;
}
// Routine Description:
// - checks if wchar is a utf16 trailing surrogate
// Arguments:
// - wch - the wchar to check
// Return Value:
// - true if wch is a trailing surrogate, false otherwise
static constexpr bool IsTrailingSurrogate(const wchar_t wch) noexcept
{
return wch >= 0xDC00 && wch <= 0xDFFF;
}
};

View File

@@ -30,7 +30,6 @@
<ClCompile Include="..\UiaTracing.cpp" />
<ClCompile Include="..\TermControlUiaTextRange.cpp" />
<ClCompile Include="..\TermControlUiaProvider.cpp" />
<ClCompile Include="..\Utf16Parser.cpp" />
<ClCompile Include="..\Viewport.cpp" />
<ClCompile Include="..\WindowBufferSizeEvent.cpp" />
<ClCompile Include="..\precomp.cpp">
@@ -52,7 +51,6 @@
<ClInclude Include="..\inc\ThemeUtils.h" />
<ClInclude Include="..\inc\utils.hpp" />
<ClInclude Include="..\inc\Viewport.hpp" />
<ClInclude Include="..\inc\Utf16Parser.hpp" />
<ClInclude Include="..\IUiaData.h" />
<ClInclude Include="..\IUiaEventDispatcher.h" />
<ClInclude Include="..\IUiaTraceable.h" />

View File

@@ -57,9 +57,6 @@
<ClCompile Include="..\GlyphWidth.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Utf16Parser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -110,9 +107,6 @@
<ClInclude Include="..\inc\CodepointWidthDetector.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Utf16Parser.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\GlyphWidth.hpp">
<Filter>Header Files</Filter>
</ClInclude>
@@ -137,9 +131,6 @@
<ClInclude Include="..\inc\Viewport.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Utf16Parser.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\precomp.h">
<Filter>Header Files</Filter>
</ClInclude>

View File

@@ -41,7 +41,6 @@ SOURCES= \
..\WindowBufferSizeEvent.cpp \
..\convert.cpp \
..\colorTable.cpp \
..\Utf16Parser.cpp \
..\utils.cpp \
..\ThemeUtils.cpp \
..\ScreenInfoUiaProviderBase.cpp \