diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4479fcd487..1ee6bf9359 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "XamlStyler.Console": { - "version": "3.2311.2", + "version": "3.2501.8", "commands": [ "xstyler" ] diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index f9a16af64d..a99422e8b0 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -11,34 +11,25 @@ colorbrewer commandlines consvc copyable -CText -dalet dcs deselection -dialytika diffing Dimidium -dje downsides -dze -dzhe Emacspeak Fitt -FTCS flac +FTCS gantt gfm ghe -gje godbolt hstrings hyperlinking hyperlinks Kbds -kje libfuzzer liga -lje Llast Lmid locl @@ -50,10 +41,8 @@ minimalistic mkmk mnt mru -nje notwrapped NTMTo -ogonek overlined perlw postmodern @@ -61,8 +50,8 @@ Powerline ptys pwn pwshw -QOL qof +QOL qps quickfix rclt @@ -75,17 +64,13 @@ rlig rubyw runtimes servicebus -shcha -similaritytolerance slnt stakeholders subpage sustainability sxn -TLDR -tonos +Tencent toolset -tshe UEFI uiatextrange und @@ -93,8 +78,5 @@ vsdevcmd westus workarounds WSLs -wtconfig XBox YBox -yeru -zhe diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 6670909587..e1307fcfd8 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -2,11 +2,8 @@ aalt abvm ACCEPTFILES ACCESSDENIED -acl -aclapi alignas alignof -allocconsolewithoptions APPLYTOSUBMENUS appxrecipe bitfield @@ -15,29 +12,20 @@ BUILDBRANCH BUILDMSG BUILDNUMBER BYCOMMAND -BYPOSITION charconv -CLASSNOTAVAILABLE CLOSEAPP cmdletbinding -COLORPROPERTY colspan COMDLG commandlinetoargv -commoncontrols -comparand COPYFROMRESOURCE cstdint CXICON CYICON -Dacl dataobject -dcomp debugbreak delayimp -DERR dlldata -DNE dnom DONTADDTORECENT DWMSBT @@ -64,81 +52,42 @@ GETHIGHCONTRAST GETMOUSEHOVERTIME GETTEXTLENGTH HARDBREAKS -Hashtable HIGHCONTRASTON HIGHCONTRASTW HIGHQUALITYSCALE hinternet -HINTERNET hotkeys href hrgn HTCLOSE hwinsta -HWINSTA -IActivation -IApp IAppearance -IAsync -IBind -IBox -IClass -IComparable -IComparer ICONINFO -IConnection -ICustom -IDialog IDirect -Idn -IExplorer -IFACEMETHOD -IFile -IGraphics -IImage IInheritable -IMap imm -IObject iosfwd -IPackage isa -ISetup isspace -IStorage istream -IStringable -ITab -ITaskbar -itow -IUri -IVirtual KEYSELECT LCID LINEBREAK -llabs -llu -localtime lround Lsa lsass LSHIFT LTGRAY MAINWINDOW -MAXIMIZEBOX medi -memchr memicmp MENUCOMMAND MENUDATA MENUINFO MENUITEMINFOW MINIMIZEBOX -mmeapi MOUSELEAVE mov -mptt -msappx MULTIPLEUSE NCHITTEST NCLBUTTONDBLCLK @@ -148,7 +97,6 @@ NCPOINTERUPDATE NCRBUTTONDBLCLK NIF NIN -NOAGGREGATION NOASYNC NOBREAKS NOCHANGEDIR @@ -161,8 +109,6 @@ NOTIFYICONDATA ntprivapi NTSYSCALLAPI numr -oaidl -ocidl ODR offsetof ofstream @@ -172,22 +118,17 @@ OSVERSIONINFOEXW otms OUTLINETEXTMETRICW overridable -PACL PAGESCROLL PALLOC PATINVERT -PEXPLICIT PICKFOLDERS PINPUT pmr -ptstr QUERYENDSESSION rcx REGCLS RETURNCMD rfind -RLO -rnrn ROOTOWNER roundf RSHIFT @@ -206,22 +147,17 @@ SHOWTIP SINGLEUSE SIZENS smoothstep -snprintf SOFTBREAK spsc -sregex SRWLOC srwlock SRWLOCK STDCPP STDMETHOD -strchr strcpy streambuf strtoul Stubless -Subheader -Subpage syscall syscolors SYSTEMBACKDROP @@ -238,23 +174,18 @@ tokeninfo tolower toupper TRACKMOUSEEVENT -TTask -TVal +ubrk UChar UFIELD ULARGE UOI UPDATEINIFILE urlmon -userenv USEROBJECTFLAGS Vcpp Viewbox virtualalloc -vsnwprintf wcsnlen -wcsstr -wcstoui WDJ winhttp wininet @@ -264,10 +195,8 @@ winstamin wmemcmp wpc WSF -wsregex WWH wwinmain -xchg XDocument XElement xfacet diff --git a/.github/actions/spelling/allow/chinese.txt b/.github/actions/spelling/allow/chinese.txt index 6b7ca2a917..02ed0e4bae 100644 --- a/.github/actions/spelling/allow/chinese.txt +++ b/.github/actions/spelling/allow/chinese.txt @@ -1,5 +1,4 @@ CHINESEBIG choseong -Jongseong -Jungseong -ssangtikeut +Choseong +CHOSEONG diff --git a/.github/actions/spelling/allow/colors.txt b/.github/actions/spelling/allow/colors.txt index b56723b368..2b7192cf5b 100644 --- a/.github/actions/spelling/allow/colors.txt +++ b/.github/actions/spelling/allow/colors.txt @@ -1,4 +1,3 @@ -alice aliceblue antiquewhite blanchedalmond @@ -39,7 +38,6 @@ gainsboro ghostwhite greenyellow hotpink -indian indianred lavenderblush lawngreen @@ -74,7 +72,6 @@ mediumvioletred midnightblue mintcream mistyrose -navajo navajowhite navyblue oldlace @@ -88,7 +85,6 @@ papayawhip peachpuff peru powderblue -rebecca rebeccapurple rosybrown royalblue @@ -109,9 +105,4 @@ webgrey webmaroon webpurple whitesmoke -xaroon -xray -xreen -xrey -xurple yellowgreen diff --git a/.github/actions/spelling/allow/fonts.txt b/.github/actions/spelling/allow/fonts.txt index c9931b2fc0..a0bacd0600 100644 --- a/.github/actions/spelling/allow/fonts.txt +++ b/.github/actions/spelling/allow/fonts.txt @@ -1,8 +1,8 @@ Consolas emoji emojis +Emojis Extralight -Gabriola Iosevka MDL Monofur diff --git a/.github/actions/spelling/allow/japanese.txt b/.github/actions/spelling/allow/japanese.txt index 261f45dc2a..6d941c08a5 100644 --- a/.github/actions/spelling/allow/japanese.txt +++ b/.github/actions/spelling/allow/japanese.txt @@ -1,4 +1 @@ -arigatoo -doomo -Kaomojis TATEGAKI diff --git a/.github/actions/spelling/allow/math.txt b/.github/actions/spelling/allow/math.txt index bf8960f008..8c6041d465 100644 --- a/.github/actions/spelling/allow/math.txt +++ b/.github/actions/spelling/allow/math.txt @@ -1,11 +1,2 @@ -atan -CPrime -HBar -HPrime isnan -LPrime -LStep -powf -RSub -sqrtf ULP diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index 940b0fc29d..f43408388b 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -4,12 +4,11 @@ advapi akv AKV altform -altforms +Altforms appendwttlogging appinstaller appx appxbundle -appxerror appxmanifest ATL autoexec @@ -25,66 +24,44 @@ CPRs cryptbase cscript DACL -DACLs defaultlib diffs disposables -dotnetfeed -DTDs -DWINRT enablewttlogging HOMESHARE Intelli issecret -IVisual libucrt libucrtd -LKG LOCKFILE +LTCG Lxss makepri -mfcribbon microsoft -microsoftonline MSAA msixbundle MSVC MSVCP mtu muxc -netcore -Onefuzz -osgvsowi -PFILETIME pgc pgo pgosweep -powerrename powershell priconfig PRIINFO propkey pscustomobject QWORD -rdpclip regedit resfiles -robocopy SACLs -sdkddkver segoe -Shobjidl sid -Skype SRW sxs symbolrequestprod Sysinternals -sysnative -systemroot -taskkill -tasklist -tdbuildteamid ucrt ucrtd unvirtualized @@ -92,12 +69,9 @@ USERDNSDOMAIN VCRT vcruntime Virtualization -visualstudio vscode VSTHRD WINBASEAPI -winsdkver -wlk wscript wslpath wtl diff --git a/.github/actions/spelling/allow/names.txt b/.github/actions/spelling/allow/names.txt index 11a1f95486..1b52d13a22 100644 --- a/.github/actions/spelling/allow/names.txt +++ b/.github/actions/spelling/allow/names.txt @@ -1,4 +1,3 @@ -Anup arkthur austdi Ballmer @@ -6,13 +5,11 @@ bhoj Bhojwani Bluloco carlos -craigloewen dhowett Diviness dsafa duhowett DXP -ekg eryksun ethanschoonover Firefox @@ -25,70 +22,43 @@ Hernan Howett Illhardt Imms -iquilezles italo jantari jerrysh Kaiyu -kimwalisch -KMehrain -Kodelife -KODELIFE -Kourosh -kowalczyk leonardder -leonmsft -Lepilleur lhecker -lukesampson -Macbook -Manandhar +Lovecraft masserano -mbadolato -Mehrain menger -mgravell -michaelniksa -michkap migrie mikegr mikemaccana -miloush miniksa nguyen niksa nvaccess nvda -oising -oldnewthing -opengl osgwiki Ottosson pabhojwa -panos +Panos paulcam -pauldotknopf PGP Pham Rincewind -rprichard Schoonover shadertoy Shomnipotence simioni -Somuah sonph sonpham stakx -talo thereses Thysell -Walisch WDX Wellons Westerman -Wirt -Wojciech zadjii Zamor zamora @@ -96,4 +66,3 @@ Zamora zljubisic Zoey zorio -Zverovich diff --git a/.github/actions/spelling/candidate.patterns b/.github/actions/spelling/candidate.patterns index 5d93035f91..d405383514 100644 --- a/.github/actions/spelling/candidate.patterns +++ b/.github/actions/spelling/candidate.patterns @@ -1,3 +1,6 @@ +# Repeated letters +\b([a-z])\g{-1}{2,}\b + # marker to ignore all code on line ^.*/\* #no-spell-check-line \*/.*$ # marker to ignore all code on line @@ -7,6 +10,9 @@ # cspell inline ^.*\b[Cc][Ss][Pp][Ee][Ll]{2}:\s*[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-[Ll][Ii][Nn][Ee]\b +# copyright +Copyright (?:\([Cc]\)|)(?:[-\d, ]|and)+(?: [A-Z][a-z]+ [A-Z][a-z]+,?)+ + # patch hunk comments ^@@ -\d+(?:,\d+|) \+\d+(?:,\d+|) @@ .* # git index header @@ -15,6 +21,9 @@ index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40} # file permissions ['"`\s][-bcdLlpsw](?:[-r][-w][-Ssx]){2}[-r][-w][-SsTtx]\+?['"`\s] +# css fonts +\bfont(?:-family|):[^;}]+ + # css url wrappings \burl\([^)]+\) @@ -32,9 +41,6 @@ index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40} # https/http/file urls (?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/*%?=~_|!:,.;]+[-A-Za-z0-9+&@#/*%=~_|] -# https/http/file urls -(?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|] - # mailto urls mailto:[-a-zA-Z=;:/?%&0-9+@._]{3,} @@ -69,6 +75,8 @@ magnet:[?=:\w]+ # Amazon \bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|) +# AWS ARN +arn:aws:[-/:\w]+ # AWS S3 \b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]* # AWS execute-api @@ -95,6 +103,8 @@ vpc-\w+ \bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]* # Google APIs \bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+ +# Google Artifact Registry +\.pkg\.dev(?:/[-\w]+)+(?::[-\w]+|) # Google Storage \b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|) # Google Calendar @@ -130,6 +140,8 @@ themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+. \bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+ # Google Colab Research Drive \bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]* +# Google Cloud regions +(?:us|(?:north|south)america|europe|asia|australia|me|africa)-(?:north|south|east|west|central){1,2}\d+ # GitHub SHAs (api) \bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b @@ -168,6 +180,12 @@ GHSA(?:-[0-9a-z]{4}){3} # GitLab commits \bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b +# #includes +^\s*#include\s*(?:<.*?>|".*?") + +# #pragma lib +^\s*#pragma comment\(lib, ".*?"\) + # binance accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]* @@ -220,7 +238,7 @@ accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]* \bmedium\.com/@?[^/\s"]+/[-\w]+ # microsoft -\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]* +\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%]* # powerbi \bapp\.powerbi\.com/reportEmbed/[^"' ]* # vs devops @@ -394,7 +412,7 @@ ipfs://[0-9a-zA-Z]{3,} \bgetopts\s+(?:"[^"]+"|'[^']+') # ANSI color codes -(?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m +(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m # URL escaped characters %[0-9A-F][A-F](?=[A-Za-z]) @@ -431,10 +449,14 @@ sha\d+:[0-9a-f]*?[a-f]{3,}[0-9a-f]* # pki (base64) LS0tLS1CRUdJT.* +# C# includes +^\s*using [^;]+; + # 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}|[iu]\d+)\b +(?:[\\0][xX]|\\u|[uU]\+|#x?|%23|&H)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b + # integrity integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1} @@ -452,7 +474,10 @@ integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1} Name\[[^\]]+\]=.* # IServiceProvider / isAThing -(?:\b|_)(?:(?:ns|)I|isA)(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b)) +(?:(?:\b|_|(?<=[a-z]))I|(?:\b|_)(?:nsI|isA))(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b)) + +# python +\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,}) # crypt (['"])\$2[ayb]\$.{56}\g{-1} @@ -466,17 +491,14 @@ Name\[[^\]]+\]=.* # machine learning (?) \b(?i)ml(?=[a-z]{2,}) -# python -\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,}) - # scrypt / argon \$(?:scrypt|argon\d+[di]*)\$\S+ # go.sum \bh1:\S+ -# scala imports -^import (?:[\w.]|\{\w*?(?:,\s*(?:\w*|\*))+\})+ +# imports +^import\s+(?:(?:static|type)\s+|)(?:[\w.]|\{\s*\w*?(?:,\s*(?:\w*|\*))+\s*\})+ # scala modules ("[^"]+"\s*%%?\s*){2,3}"[^"]+" @@ -485,7 +507,7 @@ Name\[[^\]]+\]=.* image: [-\w./:@]+ # Docker images -^\s*FROM\s+\S+:\S+(?:\s+AS\s+\S+|) +^\s*(?i)FROM\s+\S+:\S+(?:\s+AS\s+\S+|) # `docker images` REPOSITORY TAG IMAGE ID CREATED SIZE \s*\S+/\S+\s+\S+\s+[0-9a-f]{8,}\s+\d+\s+(?:hour|day|week)s ago\s+[\d.]+[KMGT]B @@ -501,6 +523,7 @@ content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1} # The `(?=.*?")` suffix should limit the false positives rate # printf #%(?:(?:(?:hh?|ll?|[jzt])?[diuoxn]|l?[cs]|L?[fega]|p)(?=[a-z]{2,})|(?:X|L?[FEGA])(?=[a-zA-Z]{2,}))(?!%)(?=[_a-zA-Z]+(?!%)\b)(?=.*?['"]) + # Alternative printf # %s %(?:s(?=[a-z]{2,}))(?!%)(?=[_a-zA-Z]+(?!%[^s])\b)(?=.*?['"]) @@ -524,7 +547,7 @@ content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1} # javascript replace regex \.replace\(/[^/\s"]{3,}/[gim]*\s*, # assign regex -= /[^*].*?(?:[a-z]{3,}|[A-Z]{3,}|[A-Z][a-z]{2,}).*/[gi]?(?=\W|$) += /[^*].*?(?:[a-z]{3,}|[A-Z]{3,}|[A-Z][a-z]{2,}).*/[gim]*(?=\W|$) # perl regex test [!=]~ (?:/.*/|m\{.*?\}|m<.*?>|m([|!/@#,;']).*?\g{-1}) @@ -538,7 +561,7 @@ perl(?:\s+-[a-zA-Z]\w*)+ (?:\d|\bh)to(?!ken)(?=[a-z])|to(?=[adhiklpun]\() # Go regular expressions -regexp?\.MustCompile\(`[^`]*`\) +regexp?\.MustCompile\((?:`[^`]*`|".*"|'.*')\) # regex choice \(\?:[^)]+\|[^)]+\) @@ -586,7 +609,7 @@ urn:shemas-jetbrains-com # xcode # xcodeproject scenes -(?:Controller|destination|ID|id)="\w{3}-\w{2}-\w{3}" +(?:Controller|destination|(?:first|second)Item|ID|id)="\w{3}-\w{2}-\w{3}" # xcode api botches customObjectInstantitationMethod @@ -601,14 +624,17 @@ PrependWithABINamepsace \.fa-[-a-z0-9]+ # bearer auth -(['"])[Bb]ear[e][r] .*?\g{-1} +(['"])[Bb]ear[e][r] .{3,}?\g{-1} # bearer auth -\b[Bb]ear[e][r]:? [-a-zA-Z=;:/0-9+.]+ +\b[Bb]ear[e][r]:? [-a-zA-Z=;:/0-9+.]{3,} # basic auth (['"])[Bb]asic [-a-zA-Z=;:/0-9+]{3,}\g{-1} +# basic auth +: [Bb]asic [-a-zA-Z=;:/0-9+.]{3,} + # base64 encoded content #([`'"])[-a-zA-Z=;:/0-9+]{3,}=\g{-1} # base64 encoded content in xml/sgml @@ -620,6 +646,9 @@ PrependWithABINamepsace # base64 encoded pkcs #\bMII[-a-zA-Z=;:/0-9+]+ +# uuencoded +#[!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_]{40,} + # DNS rr data #(?:\d+\s+){3}(?:[-+/=.\w]{2,}\s*){1,2} @@ -630,7 +659,7 @@ PrependWithABINamepsace \bnumer\b(?=.*denom) # Time Zones -\b(?:Africa|Atlantic|America|Antarctica|Asia|Australia|Europe|Indian|Pacific)(?:/\w+)+ +\b(?:Africa|Atlantic|America|Antarctica|Arctic|Asia|Australia|Europe|Indian|Pacific)(?:/[-\w]+)+ # linux kernel info ^(?:bugs|flags|Features)\s+:.* @@ -676,11 +705,17 @@ TeX/AMS "varsIgnorePattern": ".+" # nolint -nolint:\w+ +nolint:\s*[\w,]+ # Windows short paths [/\\][^/\\]{5,6}~\d{1,2}(?=[/\\]) +# Windows Resources with accelerators +\b[A-Z]&[a-z]+\b(?!;) + +# signed off by +(?i)Signed-off-by: .* + # cygwin paths /cygdrive/[a-zA-Z]/(?:Program Files(?: \(.*?\)| ?)(?:/[-+.~\\/()\w ]+)*|[-+.~\\/()\w])+ @@ -715,29 +750,29 @@ W/"[^"]+" # Compiler flags (Unix, Java/Scala) # Use if you have things like `-Pdocker` and want to treat them as `docker` -#(?:^|[\t ,>"'`=(])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) +#(?:^|[\t ,>"'`=(#])-(?:(?:J-|)[DPWXY]|[Llf])(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) # Compiler flags (Windows / PowerShell) # This is a subset of the more general compiler flags pattern. # It avoids matching `-Path` to prevent it from being treated as `ath` -#(?:^|[\t ,"'`=(])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})) +#(?:^|[\t ,"'`=(#])-(?:[DPL](?=[A-Z]{2,})|[WXYlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})) # Compiler flags (linker) ,-B # libraries -#(?:\b|_)lib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z]) - -# WWNN/WWPN (NAA identifiers) -\b(?:0x)?10[0-9a-f]{14}\b|\b(?:0x|3)?[25][0-9a-f]{15}\b|\b(?:0x|3)?6[0-9a-f]{31}\b +#(?:\b|_)[Ll]ib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z]) # iSCSI iqn (approximate regex) \biqn\.[0-9]{4}-[0-9]{2}(?:[\.-][a-z][a-z0-9]*)*\b +# WWNN/WWPN (NAA identifiers) +\b(?:0x)?10[0-9a-f]{14}\b|\b(?:0x|3)?[25][0-9a-f]{15}\b|\b(?:0x|3)?6[0-9a-f]{31}\b + # curl arguments \b(?:\\n|)curl(?:\.exe|)(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)* # set arguments -\b(?:bash|sh|set)(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)* +\b(?:bash|sh|set)(?:\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... diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt index 896211b8f1..23511535fb 100644 --- a/.github/actions/spelling/excludes.txt +++ b/.github/actions/spelling/excludes.txt @@ -97,7 +97,7 @@ Resources/(?!en) ^doc/reference/UTF8-torture-test\.txt$ ^doc/reference/windows-terminal-logo\.ans$ ^NOTICE.md -^oss/ +^oss/.*?/ ^samples/PixelShaders/Screenshots/ ^src/cascadia/TerminalSettingsEditor/SegoeFluentIconList.h$ ^src/interactivity/onecore/BgfxEngine\. @@ -121,8 +121,8 @@ Resources/(?!en) ^tools/ReleaseEngineering/ServicingPipeline\.ps1$ ^XamlStyler\.json$ ^\.github/actions/spelling/ +^\.github/workflows/spelling\d*\.yml$ ^\.vsconfig$ -^\Q.github/workflows/spelling.yml\E$ ^\Qbuild/config/release.gdnbaselines\E$ ^\Qdep/WinAppDriver/EULA.rtf\E$ ^\Qdoc/reference/windows-terminal-logo.ans\E$ @@ -133,3 +133,4 @@ Resources/(?!en) ^\Qsrc/terminal/parser/ft_fuzzwrapper/run.bat\E$ ^\Qsrc/tools/lnkd/lnkd.bat\E$ ^\Qsrc/tools/pixels/pixels.bat\E$ +^\Qsrc/cascadia/ut_app/FzfTests.cpp\E$ diff --git a/.github/actions/spelling/expect/alphabet.txt b/.github/actions/spelling/expect/alphabet.txt index 1ff5611c38..73bbdffb3c 100644 --- a/.github/actions/spelling/expect/alphabet.txt +++ b/.github/actions/spelling/expect/alphabet.txt @@ -1,38 +1,19 @@ -AAAAAABBBBBBCCC -AAAAABBBBBBCCC -abcd -ABCDEFGHIJ -abcdefghijk -ABCDEFGHIJKLMNOPQRS -ABCDEFGHIJKLMNOPQRST -ABCDEFGHIJKLMNOPQRSTUVWXY ABCG ABE -abf -BBBBB -BBBBBCCC -BBBBCCCCC +AZZ +BBDM BBGGRR -EFG +CBN +cbt +Ccc +cch efg -EFGh efgh -KLMNOQQQQQQQQQQ -QQQQQQQQQQABCDEFGHIJ -QQQQQQQQQQABCDEFGHIJKLMNOPQRS -QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ -QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ -QQQQQQQQQQABCDEFGHIJPQRST -QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ +fdw +fesb +ffd +FFFD qwerty qwertyuiopasdfg -ZAAZZ -ZABBZ -ZBAZZ -ZBBBZ -ZBBZZ ZYXWVUT -ZZBBZ -ZZZBB -ZZZBZ -ZZZZZ +zzf diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index cc53f71e5f..357606d5fa 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -2,7 +2,7 @@ aaaaabbb aabbcc ABANDONFONT abbcc -ABCF +abcc abgr ABORTIFHUNG ACCESSTOKEN @@ -26,13 +26,11 @@ AImpl AInplace ALIGNRIGHT allocing -allocs alpc ALTERNATENAME ALTF ALTNUMPAD ALWAYSTIP -aml ansicpg ANSISYS ANSISYSRC @@ -49,22 +47,17 @@ APIENTRY apiset APPBARDATA appcontainer -appium appletname -applicationmodel APPLMODAL Applocal appmodel +appshellintegration APPWINDOW APPXMANIFESTVERSION APrep -apsect APSTUDIO -archeologists -Argb ARRAYSIZE ARROWKEYS -asan ASBSET ASetting ASingle @@ -72,11 +65,11 @@ ASYNCDONTCARE ASYNCWINDOWPOS atch ATest +atg aumid Authenticode AUTOBUDDY AUTOCHECKBOX -autocrlf autohide AUTOHSCROLL automagically @@ -86,16 +79,13 @@ autoscrolling Autowrap AVerify awch -AZCOPY azurecr AZZ backgrounded Backgrounder backgrounding -backported backstory Bazz -bbb bbccb BBDM bbwe @@ -123,13 +113,13 @@ bitmasks BITOPERATION BKCOLOR BKGND +BKMK Bksp Blt blu BLUESCROLL bmi bodgy -BODGY BOLDFONT Borland boutput @@ -148,9 +138,7 @@ buflen buildsystems buildtransitive BValue -bytebuffer -cac -cacafire +Cacafire CALLCONV CANDRABINDU capslock @@ -161,12 +149,6 @@ catid cazamor CBash cbiex -CBN -cbt -Ccc -CCCBB -CCCDDD -cch CCHAR CCmd ccolor @@ -181,15 +163,14 @@ cfie cfiex cfte CFuzz +cgmanifest cgscrn chafa changelists CHARSETINFO -chh chshdng CHT CLASSSTRING -CLE cleartype CLICKACTIVE clickdown @@ -212,9 +193,7 @@ cmw CNL cnn Codeflow -codenav codepages -codepath coinit colorizing COLORONCOLOR @@ -226,13 +205,8 @@ colortbl colortest colortool COLORVALUE -combaseapi comctl -commandline -commctrl commdlg -COMMITID -componentization conapi conattrs conbufferout @@ -250,7 +224,6 @@ conintegrityuwp coninteractivitybase coninteractivityonecore coninteractivitywin -conio coniosrv CONKBD conlibk @@ -263,13 +236,13 @@ conpropsp conpty conptylib conserv +consoleaccessibility consoleapi CONSOLECONTROL CONSOLEENDTASK consolegit consolehost CONSOLEIME -consoleinternal CONSOLESETFOREGROUND consoletaeftemplates consoleuwp @@ -277,7 +250,6 @@ Consolewait CONSOLEWINDOWOWNER consrv constexprable -constness contentfiles conterm contsf @@ -311,7 +283,6 @@ csbi csbiex CSHORT Cspace -csrmsg CSRSS csrutil CSTYLE @@ -368,16 +339,14 @@ DBGFONTS DBGOUTPUT dbh dblclk +DBUILD Dcd DColor -dcommon +DCOMMON DComposition -DDDCCC -dde DDESHARE DDevice DEADCHAR -dealloc Debian debugtype DECAC @@ -482,18 +451,16 @@ DELAYLOAD DELETEONRELEASE depersist deprioritized -deserializers -desktopwindowxamlsource devicecode Dext DFactory DFF dialogbox +DINLINE directio DIRECTX DISABLEDELAYEDEXPANSION DISABLENOSCROLL -DISPATCHNOTIFY DISPLAYATTRIBUTE DISPLAYCHANGE distros @@ -530,9 +497,10 @@ dsm dsound DSSCL DSwap -DTest DTo DTTERM +DUNICODE +DUNIT dup'ed dvi dwl @@ -542,8 +510,6 @@ dwmapi DWORDs dwrite dxgi -dxgidwm -dxinterop dxsm dxttbmp Dyreen @@ -557,7 +523,6 @@ EDITKEYS EDITTEXT EDITUPDATE Efast -efghijklmn EHsc EINS ELEMENTNOTAVAILABLE @@ -566,7 +531,6 @@ enabledelayedexpansion ENDCAP endptr ENTIREBUFFER -entrypoints ENU ENUMLOGFONT ENUMLOGFONTEX @@ -581,7 +545,6 @@ esrp ESV ETW EUDC -EVENTID eventing evflags evt @@ -603,16 +566,6 @@ FACESIZE FAILIFTHERE fastlink fcharset -FDEA -fdw -FECF -FEEF -fesb -FFAF -ffd -FFDE -FFFD -FFFDb fgbg FGCOLOR FGHIJ @@ -630,7 +583,6 @@ FINDDLG FINDDOWN FINDREGEX FINDSTRINGEXACT -FINDUP FITZPATRICK FIXEDFILEINFO Flg @@ -651,6 +603,7 @@ FONTSTRING FONTTYPE FONTWIDTH FONTWINDOW +foob FORCEOFFFEEDBACK FORCEONFEEDBACK FRAMECHANGED @@ -664,13 +617,14 @@ Ftm Fullscreens Fullwidth FUNCTIONCALL -fuzzer fuzzmain fuzzmap fuzzwrapper +fuzzyfinder fwdecl fwe fwlink +fzf gci gcx gdi @@ -726,6 +680,7 @@ GETWHEELSCROLLCHARS GETWHEELSCROLLLINES Gfun gfx +gfycat GGI GHgh GHIJK @@ -746,6 +701,7 @@ Greyscale gridline gset gsl +Guake guc GUIDATOM GValue @@ -775,7 +731,6 @@ hfind hfont hfontresource hglobal -hhh hhook hhx HIBYTE @@ -791,7 +746,6 @@ HKCU hkey hkl HKLM -hlocal hlsl HMB HMK @@ -810,7 +764,6 @@ HREDRAW hresult hscroll hstr -hstring HTBOTTOMLEFT HTBOTTOMRIGHT HTCAPTION @@ -839,14 +792,12 @@ idl idllib IDOK IDR -idth IDTo IDXGI IFACEMETHODIMP ification IGNORELANGUAGE iid -IInput IIo ILC ILCo @@ -860,20 +811,18 @@ INFOEX inheritcursor INITCOMMONCONTROLSEX INITDIALOG -initguid +INITGUID INITMENU inkscape INLINEPREFIX inproc Inputkeyinfo -inputpaneinterop Inputreadhandledata INPUTSCOPE INSERTMODE INTERACTIVITYBASE INTERCEPTCOPYPASTE INTERNALNAME -Interner intsafe INVALIDARG INVALIDATERECT @@ -885,8 +834,6 @@ itermcolors itf Ith IUI -IUnknown -ivalid IWIC IXP jconcpp @@ -910,7 +857,6 @@ keydowns KEYFIRST KEYLAST Keymapping -keyscan keystate keyups Kickstart @@ -920,10 +866,6 @@ kinda KIYEOK KLF KLMNO -KLMNOPQRST -KLMNOPQRSTQQQQQ -KLMNOPQRSTUVWXY -KLMNOPQRSTY KOK KPRIORITY KVM @@ -945,6 +887,8 @@ LCONTROL LCTRL lcx LEFTALIGN +libsancov +libtickit LIMITTEXT LINEDOWN LINESELECTION @@ -1049,6 +993,7 @@ MBUTTONDOWN MBUTTONUP mdmerge MDs +mdtauk MEASUREITEM megamix memallocator @@ -1058,7 +1003,6 @@ MENUCONTROL MENUDROPALIGNMENT MENUITEMINFO MENUSELECT -messageext metaproj Mgrs microsoftpublicsymbols @@ -1075,11 +1019,9 @@ minwindef MMBB mmcc MMCPL -mmsystem MNC MNOPQ MNOPQR -MNOPQRSTUVWXY MODALFRAME MODERNCORE MONITORINFO @@ -1091,7 +1033,6 @@ MOUSEHWHEEL MOVESTART msb msbuildcache -msctf msctls msdata MSDL @@ -1106,10 +1047,9 @@ MSGSELECTMODE msiexec MSIL msix -msrc +MSRC MSVCRTD MTSM -Munged murmurhash muxes myapplet @@ -1148,7 +1088,6 @@ Newtonsoft NEXTLINE nfe NLSMODE -nnn NOACTIVATE NOAPPLYNOW NOCLIP @@ -1201,40 +1140,31 @@ NPFS nrcs NSTATUS ntapi -ntcon -ntcsrdll ntdef NTDEV ntdll ntifs -ntlpcapi ntm -ntrtl ntstatus nttree -nturtl ntuser NTVDM -ntverp nugetversions NUKTA nullness nullonfailure nullopts -numlock NUMSCROLL NUnit nupkg NVIDIA NVT OACR -objbase ocolor oemcp OEMFONT OEMFORMAT OEMs -offboarded OLEAUT OLECHAR onebranch @@ -1248,6 +1178,7 @@ onecoreuuid ONECOREWINDOWS onehalf oneseq +oob openbash opencode opencon @@ -1283,7 +1214,6 @@ PALPC pankaj parentable PATCOPY -pathcch PATTERNID pbstr pcb @@ -1338,7 +1268,6 @@ phicon phwnd pidl PIDLIST -pids pii piml pimpl @@ -1363,11 +1292,9 @@ POINTERUPDATE POINTSLIST policheck POLYTEXTW -poppack POPUPATTR popups PORFLG -positionals POSTCHARBREAKS POSX POSXSCROLL @@ -1400,11 +1327,9 @@ PREVLINE prg pri prioritization -processenv processhost PROCESSINFOCLASS PRODEXT -Productize PROPERTYID PROPERTYKEY propertyval @@ -1416,16 +1341,12 @@ propsys PROPTITLE propvar propvariant -propvarutil psa -PSCRED PSECURITY pseudoconsole -pseudoterminal psh pshn PSHNOTIFY -pshpack PSINGLE psl psldl @@ -1497,8 +1418,6 @@ REGISTERVDM regkey REGSTR RELBINPATH -remoting -renamer rendersize reparented reparenting @@ -1533,19 +1452,16 @@ RIGHTALIGN RIGHTBUTTON riid ris -roadmap robomac rodata rosetta RRF -rrr RRRGGGBB rsas rtcore RTEXT RTLREADING Rtn -ruleset runas RUNDLL runformat @@ -1563,7 +1479,6 @@ rvpa RWIN rxvt safemath -sancov sba SBCS SBCSDBCS @@ -1591,10 +1506,8 @@ SCROLLSCREENBUFFER scursor sddl SDKDDK -securityappcontainer segfault SELCHANGE -SELECTALL SELECTEDFONT SELECTSTRING Selfhosters @@ -1638,14 +1551,10 @@ SFUI sgr SHCo shcore -shellapi shellex -shellscalingapi SHFILEINFO SHGFI SHIFTJIS -shlguid -shlobj shlwapi SHORTPATH SHOWCURSOR @@ -1683,7 +1592,6 @@ snapcy snk SOLIDBOX Solutiondir -somefile sourced SRCAND SRCCODEPAGE @@ -1715,9 +1623,8 @@ STDEXT STDMETHODCALLTYPE STDMETHODIMP STGM -Stringable STRINGTABLE -strsafe +STRSAFE STUBHEAD STUVWX stylecop @@ -1743,32 +1650,27 @@ SYSLIB SYSLINK SYSMENU sysparams -sysparamsext SYSTEMHAND SYSTEMMENU SYSTEMTIME tabview -TAdd taef TARG targetentrypoint TARGETLIBS TARGETNAME targetver -TBase tbc tbi Tbl TBM -tchar +TCHAR TCHFORMAT TCI tcommands tdbuild Tdd -TDelegated TDP -tearoff Teb Techo tellp @@ -1778,7 +1680,6 @@ terminalinput terminalrenderdata TERMINALSCROLLING terminfo -TEs testcon testd testenvs @@ -1790,7 +1691,6 @@ TESTNULL testpass testpasses TEXCOORD -TExpected textattribute TEXTATTRIBUTEID textboxes @@ -1801,39 +1701,28 @@ TEXTMETRIC TEXTMETRICW textmode texttests -TFunction THUMBPOSITION THUMBTRACK -tickit -TIcon tilunittests titlebars TITLEISLINKNAME -TJson -TLambda TLDP TLEN TMAE TMPF -TMult tmultiple -TODOs tofrom -tokenhelpers toolbars TOOLINFO TOOLWINDOW TOPDOWNDIB -TOpt tosign -touchpad tracelogging traceviewpp trackbar trackpad transitioning Trd -TREX triaged triaging TRIMZEROHEADINGS @@ -1841,9 +1730,7 @@ trx tsa tsgr tsm -TStr TSTRFORMAT -TSub TTBITMAP TTFONT TTFONTLIST @@ -1852,7 +1739,6 @@ TTo tvpp tvtseq TYUI -UAC uap uapadmin UAX @@ -1888,7 +1774,6 @@ unk unknwn UNORM unparseable -Unregistering untextured UPDATEDISPLAY UPDOWN @@ -1906,7 +1791,6 @@ USEFILLATTRIBUTE USEGLYPHCHARS USEHICON USEPOSITION -USERDATA userdpiapi Userp userprivapi @@ -1919,7 +1803,6 @@ USRDLL utext utr UVWXY -UVWXYZ uwa uwp uwu @@ -1928,17 +1811,16 @@ Vanara vararg vclib vcxitems -vectorize VERCTRL VERTBAR VFT vga vgaoem viewkind -viewports VIRAMA Virt VIRTTERM +visualstudiosdk vkey VKKEYSCAN VMs @@ -1997,7 +1879,6 @@ wekyb wewoad wex wextest -wextestclass WFill wfopen WHelper @@ -2008,9 +1889,7 @@ Wiggum wil WImpl WINAPI -winbase winbasep -wincodec wincon winconp winconpty @@ -2025,10 +1904,8 @@ windll WINDOWALPHA windowdpiapi WINDOWEDGE -windowext WINDOWINFO windowio -windowmetrics WINDOWPLACEMENT windowpos WINDOWPOSCHANGED @@ -2036,20 +1913,15 @@ WINDOWPOSCHANGING windowproc windowrect windowsapp -windowsinternalstring WINDOWSIZE windowsshell windowsterminal -windowsx windowtheme winevent -wingdi winget wingetcreate WINIDE -winioctl winmd -winmeta winmgr winmm WINMSAPP @@ -2059,8 +1931,8 @@ WInplace winres winrt winternl +winui winuser -winuserp WINVER wistd wmain @@ -2097,10 +1969,9 @@ WRITECONSOLEINPUT WRITECONSOLEOUTPUT WRITECONSOLEOUTPUTSTRING wrkstr -wrl +WRL wrp WRunoff -wsl WSLENV wstr wstrings @@ -2113,7 +1984,7 @@ wtof WTs WTSOFTFONT wtw -wtypes +Wtypes WUX WVerify WWith @@ -2132,7 +2003,6 @@ XBUTTONDOWN XBUTTONUP XCast XCENTER -xchar xcopy XCount xdy @@ -2142,6 +2012,7 @@ XFG XFile XFORM XIn +xkcd XManifest XMath XNamespace @@ -2168,7 +2039,6 @@ YLimit YPan YSubstantial YVIRTUALSCREEN -Zab zabcd Zabcdefghijklmn Zabcdefghijklmnopqrstuvwxyz @@ -2177,4 +2047,3 @@ ZCtrl ZWJs ZYXWVU ZYXWVUTd -zzf diff --git a/.github/actions/spelling/line_forbidden.patterns b/.github/actions/spelling/line_forbidden.patterns index 5617a8af5f..ecfead2f0c 100644 --- a/.github/actions/spelling/line_forbidden.patterns +++ b/.github/actions/spelling/line_forbidden.patterns @@ -8,6 +8,24 @@ # you might not want to check in code where you skip all the other tests. #\bfit\( +# English does not use a hyphen between adverbs and nouns +# https://twitter.com/nyttypos/status/1894815686192685239 +(?:^|\s)[A-Z]?[a-z]+ly-(?=[a-z]{3,})(?:[.,?!]?\s|$) + +# Don't use `requires that` + `to be` +# https://twitter.com/nyttypos/status/1894816551435641027 +\brequires that \w+\b[^.]+to be\b + +# A fully parenthetical sentence’s period goes inside the parentheses, not outside. +# https://twitter.com/nyttypos/status/1898844061873639490 +\([A-Z][a-z]{2,}(?: [a-z]+){3,}\)\.\s + +# Complete sentences shouldn't be in the middle of another sentence as a parenthetical. +(? In formal writing and where contractions are frowned upon, use `cannot`. # > It is possible to write `can not`, but you generally find it only as part of some other construction, such as `not only . . . but also.` # - if you encounter such a case, add a pattern for that case to patterns.txt. -\b[Cc]an not\b +\b[Cc]an not\b(?! only\b) + +# Should be `chart` +(?i)\bhelm\b.*\bchard\b # Do not use `(click) here` links # For more information, see: @@ -56,9 +111,27 @@ # * https://heyoka.medium.com/dont-use-click-here-f32f445d1021 (?i)(?:>|\[)(?:(?:click |)here|link|(?:read |)more)(?:> /etc/apt/sources.list.d/something-distro.list +# ```` +\bapt-key add\b + +# Should be `nearby` +\bnear by\b + # Should probably be a person named `Nick` or the abbreviation `NIC` \bNic\b @@ -153,7 +260,7 @@ \bperform it's\b # Should be `opt-in` -#(? below for the` +(?i)\bfind below the\b + +# Should be `then any` unless there's a comparison before the `,` +, than any\b + # Should be `did not exist` \bwere not existent\b @@ -197,9 +357,18 @@ # Should be `nonexistent` \b[Nn]o[nt][- ]existent\b +# Should be `our` +\bspending out time\b + # Should be `@brief` / `@details` / `@param` / `@return` / `@retval` (?:^\s*|(?:\*|//|/*)\s+`)[\\@](?:breif|(?:detail|detials)|(?:params(?!\.)|prama?)|ret(?:uns?)|retvl)\b +# Should be `more than` or `more, then` +\bmore then\b + +# Should be `Pipeline`/`pipeline` +(?:(?<=\b|[A-Z])p|P)ipeLine(?:\b|(?=[A-Z])) + # Should be `preexisting` [Pp]re[- ]existing @@ -224,14 +393,27 @@ # Should be `reentrant` [Rr]e[- ]entrant +# Should be `room for` +\brooms for (?!lease|rent|sale) + +# Should be `socioeconomic` +# https://dictionary.cambridge.org/us/dictionary/english/socioeconomic +socio-economic + # Should be `strong suit` \b(?:my|his|her|their) strong suite\b +# Should probably be `temperatures` unless actually talking about thermal drafts (things birds may fly on) +\bthermals\b + +# Should be `there are` or `they are` (or `they're`) +(?i)\btheir are\b + # Should be `understand` \bunder stand\b -# Should be `URI` or `uri` unless it refers to a person named `Uri` -#(?|".*?") + +# hit-count: 1131 file-count: 326 +# C# includes +^\s*using [^;]+; + +# hit-count: 128 file-count: 47 # C network byte conversions (?:\d|\bh)to(?!ken)(?=[a-z])|to(?=[adhiklpun]\() -# hit-count: 59 file-count: 36 -# IServiceProvider / isAThing -(?:\b|_)(?:(?:ns|)I|isA)(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b)) +# hit-count: 53 file-count: 10 +# ANSI color codes +(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m -# hit-count: 9 file-count: 6 +# hit-count: 45 file-count: 29 +# version suffix v# +(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) + +# hit-count: 30 file-count: 19 +# uuid: +\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b + +# hit-count: 17 file-count: 7 +# File extensions +\*\.[+\w]+, + +# hit-count: 15 file-count: 9 # Markdown anchor links \(#\S*?[a-zA-Z]\S*?\) -# hit-count: 5 file-count: 5 -# libraries -(?:\b|_)lib(?:re(?=office)|)(?!era[lt]|ero|ert(?:ies|y)|rar(?:i(?:an|es)|y))(?=[a-z]) +# hit-count: 14 file-count: 8 +# hex runs +\b[0-9a-fA-F]{16,}\b + +# hit-count: 12 file-count: 8 +# hex digits including css/html color classes: +(?:[\\0][xX]|\\u|[uU]\+|#x?|%23|&H)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b + +# hit-count: 12 file-count: 7 +# tar arguments +\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ + +# hit-count: 9 file-count: 6 +# Repeated letters +\b([a-z])\g{-1}{2,}\b + +# hit-count: 8 file-count: 2 +# regex choice +\(\?:[^)]+\|[^)]+\) + +# hit-count: 8 file-count: 1 +# latex (check-spelling >= 0.0.22) +\\\w{2,}\{ + +# hit-count: 7 file-count: 4 +# Python string prefix / binary prefix +# 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 +(?)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|"'`=(])-(?:D(?=[A-Z])|W(?!ork)|X|f(?=[ms]))(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) - -# hit-count: 60 file-count: 35 -# version suffix v# -(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) - -# hit-count: 2 file-count: 2 -# This does not cover multiline strings, if your repository has them, -# you'll want to remove the `(?=.*?")` suffix. -# The `(?=.*?")` suffix should limit the false positives rate -# printf -%(?:s)(?!ize)(?=[a-z]{2,}) - -# hit-count: 16 file-count: 10 -# uuid: -\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b - -# hit-count: 13 file-count: 4 -# Non-English -[a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,} - -# hit-count: 7 file-count: 5 -# 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}|[iu]\d+)\b - -# hit-count: 7 file-count: 1 -# regex choice -\(\?:[^)]+\|[^)]+\) - -# hit-count: 4 file-count: 4 -# tar arguments -\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ - -# hit-count: 4 file-count: 1 -# ANSI color codes -(?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m - -# hit-count: 4 file-count: 1 -# 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|[,.])* - -# hit-count: 3 file-count: 3 -# mailto urls -mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,} - -# hit-count: 2 file-count: 1 -# Python string prefix / binary prefix -# 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 -(?= 0.0.22) -\\\w{2,}\{ - -# hit-count: 1 file-count: 1 -# 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 - -# Questionably acceptable forms of `in to` -# Personally, I prefer `log into`, but people object -# https://www.tprteaching.com/log-into-log-in-to-login/ -\b(?:[Ll]og|[Ss]ign) in to\b - -# to opt in -\bto opt in\b # Questionably acceptable forms of `in to` # Personally, I prefer `log into`, but people object @@ -183,6 +210,10 @@ mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,} # to opt in \bto opt in\b + +# pass(ed|ing) in +\bpass(?:ed|ing) in\b + # acceptable duplicates # ls directory listings [-bcdlpsw](?:[-r][-w][-SsTtx]){3}[\.+*]?\s+\d+\s+\S+\s+\S+\s+[.\d]+(?:[KMGT]|)\s+ diff --git a/.github/actions/spelling/reject.txt b/.github/actions/spelling/reject.txt index d98038f96f..a4022cd4b4 100644 --- a/.github/actions/spelling/reject.txt +++ b/.github/actions/spelling/reject.txt @@ -1,10 +1,19 @@ ^attache$ ^attacher$ ^attachers$ -^bellow$ +^bellow?$ benefitting occurences? ^dependan.* +^develope$ +^developement$ +^developpe +^Devers?$ +^devex +^devide +^Devinn?[ae] +^devisal +^devisor ^diables?$ ^oer$ Sorce @@ -12,4 +21,5 @@ Sorce ^Teh$ ^untill$ ^untilling$ +^venders?$ ^wether.* diff --git a/.github/workflows/spelling2.yml b/.github/workflows/spelling2.yml index 7675d5d004..9c9cb9ea85 100644 --- a/.github/workflows/spelling2.yml +++ b/.github/workflows/spelling2.yml @@ -93,7 +93,7 @@ jobs: steps: - name: check-spelling id: spelling - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@v0.0.25 with: suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} checkout: true @@ -104,7 +104,7 @@ jobs: report-timing: 1 warnings: bad-regex,binary-file,deprecated-feature,ignored-expect-variant,large-file,limited-references,no-newline-at-eof,noisy-file,non-alpha-in-dictionary,token-is-substring,unexpected-line-ending,whitespace-in-dictionary,minified-file,unsupported-configuration,no-files-to-check,unclosed-block-ignore-begin,unclosed-block-ignore-end experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} - use_sarif: ${{ (!github.event.pull_request || (github.event.pull_request.head.repo.full_name == github.repository)) && 1 }} + use_sarif: 1 check_extra_dictionaries: "" dictionary_source_prefixes: > { @@ -114,34 +114,33 @@ jobs: cspell:software-terms/softwareTerms.txt cspell:cpp/stdlib-cpp.txt cspell:cpp/stdlib-c.txt - cspell:lorem-ipsum/dictionary.txt + cspell:python/python/python-lib.txt cspell:php/php.txt + cspell:node/node.txt + cspell:dart/dart.txt cspell:filetypes/filetypes.txt cspell:java/java.txt - cspell:node/node.txt - cspell:golang/go.txt - cspell:java/java-terms.txt - cspell:mnemonics/mnemonics.txt + cspell:css/css.txt + cspell:dotnet/dotnet.txt cspell:npm/npm.txt cspell:fullstack/fullstack.txt - cspell:python/python/python-lib.txt - cspell:dotnet/dotnet.txt - cspell:dart/dart.txt - cspell:aws/aws.txt - cspell:python/common/extra.txt - cspell:css/css.txt + cspell:java/java-terms.txt + cspell:r/r.txt + cspell:golang/go.txt cspell:cpp/stdlib-cmath.txt cspell:typescript/typescript.txt + cspell:html/html.txt cspell:cpp/compiler-msvc.txt cspell:django/django.txt - cspell:html/html.txt - cspell:cpp/lang-keywords.txt + cspell:aws/aws.txt + cspell:python/common/extra.txt cspell:cpp/ecosystem.txt - cspell:r/r.txt - cspell:cpp/compiler-clang-attributes.txt - cspell:powershell/powershell.txt + cspell:cpp/lang-keywords.txt cspell:csharp/csharp.txt + cspell:cpp/compiler-clang-attributes.txt cspell:python/python/python.txt + cspell:mnemonics/mnemonics.txt + cspell:powershell/powershell.txt comment-push: name: Report (Push) @@ -154,7 +153,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' steps: - name: comment - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@v0.0.25 with: checkout: true spell_check_this: microsoft/terminal@main @@ -172,7 +171,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@v0.0.25 with: checkout: true spell_check_this: microsoft/terminal@main @@ -198,7 +197,7 @@ jobs: cancel-in-progress: false steps: - name: apply spelling updates - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@v0.0.25 with: experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} checkout: true diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 63464ea4fe..4adf3837b1 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -1,4 +1,4 @@ -name: Publish to Winget +name: Publish to WinGet on: release: @@ -6,6 +6,9 @@ on: env: REGEX: 'Microsoft\.WindowsTerminal(?:Preview)?_([\d.]+)_8wekyb3d8bbwe\.msixbundle$' + # winget-create will read the following environment variable to access the GitHub token needed for submitting a PR + # See https://aka.ms/winget-create-token + WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_TOKEN }} jobs: publish: @@ -21,4 +24,4 @@ jobs: $wingetPackage = "Microsoft.WindowsTerminal${{ github.event.release.prerelease && '.Preview' || '' }}" & curl.exe -JLO https://aka.ms/wingetcreate/latest - & .\wingetcreate.exe update $wingetPackage -s -v $version -u $wingetRelevantAsset.browser_download_url -t "${{ secrets.WINGET_TOKEN }}" + & .\wingetcreate.exe update $wingetPackage -s -v $version -u $wingetRelevantAsset.browser_download_url diff --git a/.vsconfig b/.vsconfig index 4d39a491cc..a3997ad694 100644 --- a/.vsconfig +++ b/.vsconfig @@ -17,7 +17,6 @@ "Microsoft.VisualStudio.Component.AppInsights.Tools", "Microsoft.Net.Component.4.8.TargetingPack", "Microsoft.VisualStudio.Component.DiagnosticTools", - "Microsoft.NetCore.Component.Runtime.6.0", "Microsoft.VisualStudio.Component.ClassDesigner", "Microsoft.VisualStudio.Component.GraphDocument", "Microsoft.VisualStudio.Component.CodeMap", diff --git a/NOTICE.md b/NOTICE.md index bd99dd939c..efe962bc0d 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -285,6 +285,8 @@ specific language governing permissions and limitations under the License. **Source**: [https://github.com/commonmark/cmark](https://github.com/commonmark/cmark) ### License + +``` Copyright (c) 2014, John MacFarlane All rights reserved. @@ -455,6 +457,36 @@ 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. +``` + +## fzf + +### License + +``` +The MIT License (MIT) + +Copyright (c) 2013-2024 Junegunn Choi +Copyright (c) 2021-2025 Simon Hauser + +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. +``` # Microsoft Open Source diff --git a/build/Helix/HelixTestHelpers.cs b/build/Helix/HelixTestHelpers.cs index 67f13937c0..cb8591031e 100644 --- a/build/Helix/HelixTestHelpers.cs +++ b/build/Helix/HelixTestHelpers.cs @@ -28,7 +28,7 @@ namespace HelixTestHelpers public List Screenshots { get; private set; } public List RerunResults { get; private set; } } - + // // Azure DevOps doesn't currently provide a way to directly report sub-results for tests that failed at least once // that were run multiple times. To get around that limitation, we'll mark the test as "Skip" since @@ -49,46 +49,46 @@ namespace HelixTestHelpers // TODO (https://github.com/dotnet/arcade/issues/2773): Once we're able to directly report things in a // more granular fashion than just a binary pass/fail result, we should do that. // - [DataContract] + [DataContract] internal class JsonSerializableTestResults - { + { [DataMember] internal string blobPrefix; - + [DataMember] internal string blobSuffix; - + [DataMember] internal string[] errors; - + [DataMember] internal JsonSerializableTestResult[] results; } - - [DataContract] - internal class JsonSerializableTestResult + + [DataContract] + internal class JsonSerializableTestResult { [DataMember] internal string outcome; [DataMember] internal int duration; - + [DataMember(EmitDefaultValue = false)] internal string log; - + [DataMember(EmitDefaultValue = false)] internal string[] screenshots; - + [DataMember(EmitDefaultValue = false)] internal int errorIndex; } - + public class TestPass { public TimeSpan TestPassExecutionTime { get; set; } public List TestResults { get; set; } - + public static TestPass ParseTestWttFile(string fileName, bool cleanupFailuresAreRegressions, bool truncateTestNames) { using (var stream = File.OpenRead(fileName)) @@ -174,7 +174,7 @@ namespace HelixTestHelpers if (testsExecuting == 1) { string testName = element.Attribute("Title").Value; - + if (truncateTestNames) { const string xamlNativePrefix = "Windows::UI::Xaml::Tests::"; @@ -243,7 +243,7 @@ namespace HelixTestHelpers // The test cleanup errors will often come after the test claimed to have - // 'passed'. We treat them as errors as well. + // 'passed'. We treat them as errors as well. if (inTestCleanup) { currentResult.CleanupPassed = false; @@ -292,7 +292,7 @@ namespace HelixTestHelpers foreach(var screenshot in screenshots) { string fileNameSuffix = string.Empty; - + if (fileName.Contains("_rerun_multiple")) { fileNameSuffix = "_rerun_multiple"; @@ -301,7 +301,7 @@ namespace HelixTestHelpers { fileNameSuffix = "_rerun"; } - + currentResult.Screenshots.Add(screenshot.Replace(".jpg", fileNameSuffix + ".jpg")); } } @@ -313,7 +313,7 @@ namespace HelixTestHelpers testPassStopTime = Int64.Parse(doc.Root.Descendants("WexTraceInfo").Last().Attribute("TimeStamp").Value); var testPassTime = TimeSpan.FromSeconds((double)(testPassStopTime - testPassStartTime) / frequency); - + foreach (TestResult testResult in testResults) { if (testResult.Details != null) @@ -331,13 +331,13 @@ namespace HelixTestHelpers return testpass; } } - + public static TestPass ParseTestWttFileWithReruns(string fileName, string singleRerunFileName, string multipleRerunFileName, bool cleanupFailuresAreRegressions, bool truncateTestNames) { TestPass testPass = ParseTestWttFile(fileName, cleanupFailuresAreRegressions, truncateTestNames); TestPass singleRerunTestPass = File.Exists(singleRerunFileName) ? ParseTestWttFile(singleRerunFileName, cleanupFailuresAreRegressions, truncateTestNames) : null; TestPass multipleRerunTestPass = File.Exists(multipleRerunFileName) ? ParseTestWttFile(multipleRerunFileName, cleanupFailuresAreRegressions, truncateTestNames) : null; - + List rerunTestResults = new List(); if (singleRerunTestPass != null) @@ -377,9 +377,9 @@ namespace HelixTestHelpers public static void OutputFailedTestQuery(string wttInputPath) { var testPass = TestPass.ParseTestWttFile(wttInputPath, cleanupFailuresAreRegressions: true, truncateTestNames: false); - + List failedTestNames = new List(); - + foreach (var result in testPass.TestResults) { if (!result.Passed) @@ -387,23 +387,23 @@ namespace HelixTestHelpers failedTestNames.Add(result.Name); } } - + if (failedTestNames.Count > 0) { string failedTestSelectQuery = "(@Name='"; - + for (int i = 0; i < failedTestNames.Count; i++) { failedTestSelectQuery += failedTestNames[i]; - + if (i < failedTestNames.Count - 1) { failedTestSelectQuery += "' or @Name='"; } } - + failedTestSelectQuery += "')"; - + Console.WriteLine(failedTestSelectQuery); } else @@ -418,7 +418,7 @@ namespace HelixTestHelpers private string testNamePrefix; private string helixResultsContainerUri; private string helixResultsContainerRsas; - + public TestResultParser(string testNamePrefix, string helixResultsContainerUri, string helixResultsContainerRsas) { this.testNamePrefix = testNamePrefix; @@ -430,7 +430,7 @@ namespace HelixTestHelpers { Dictionary subResultsJsonByMethod = new Dictionary(); TestPass testPass = TestPass.ParseTestWttFileWithReruns(wttInputPath, wttSingleRerunInputPath, wttMultipleRerunInputPath, cleanupFailuresAreRegressions: true, truncateTestNames: false); - + foreach (var result in testPass.TestResults) { var methodName = result.Name.Substring(result.Name.LastIndexOf('.') + 1); @@ -488,7 +488,7 @@ namespace HelixTestHelpers int resultCount = results.Count; int passedCount = results.Where(r => r.Passed).Count(); - + // Since we re-run tests on failure, we'll mark every test that failed at least once as "skipped" rather than "failed". // If the test failed sufficiently often enough for it to count as a failed test (determined by a property on the // Azure DevOps job), we'll later mark it as failed during test results processing. @@ -504,15 +504,15 @@ namespace HelixTestHelpers assembly.SetAttributeValue("run-date", DateTime.Now.ToString("yyyy-MM-dd")); // This doesn't need to be completely accurate since it's not exposed anywhere. - // If we need accurate an start time we can probably calculate it from the te.wtl file, but for + // If we need an accurate start time we can probably calculate it from the te.wtl file, but for // now this is fine. assembly.SetAttributeValue("run-time", (DateTime.Now - testPass.TestPassExecutionTime).ToString("hh:mm:ss")); - + assembly.SetAttributeValue("total", resultCount); assembly.SetAttributeValue("passed", passedCount); assembly.SetAttributeValue("failed", failedCount); assembly.SetAttributeValue("skipped", skippedCount); - + assembly.SetAttributeValue("time", (int)testPass.TestPassExecutionTime.TotalSeconds); assembly.SetAttributeValue("errors", 0); root.Add(assembly); @@ -537,9 +537,9 @@ namespace HelixTestHelpers test.SetAttributeValue("method", methodName); test.SetAttributeValue("time", result.ExecutionTime.TotalSeconds); - + string resultString = string.Empty; - + if (result.Passed && !result.Skipped) { resultString = "Pass"; @@ -554,7 +554,7 @@ namespace HelixTestHelpers resultString = "Fail"; } - + if (!result.Passed) { if (result.Skipped) @@ -579,36 +579,36 @@ namespace HelixTestHelpers File.WriteAllText(xunitOutputPath, root.ToString()); } - + private JsonSerializableTestResult ConvertToSerializableResult(TestResult rerunResult, string[] uniqueErrors) { var serializableResult = new JsonSerializableTestResult(); - + serializableResult.outcome = rerunResult.Passed ? "Passed" : "Failed"; serializableResult.duration = (int)Math.Round(rerunResult.ExecutionTime.TotalMilliseconds); - + if (!rerunResult.Passed) { serializableResult.log = Path.GetFileName(rerunResult.SourceWttFile); - + if (rerunResult.Screenshots.Any()) { List screenshots = new List(); - + foreach (var screenshot in rerunResult.Screenshots) { screenshots.Add(Path.GetFileName(screenshot)); } - + serializableResult.screenshots = screenshots.ToArray(); } - + // To conserve space, we'll log the index of the error to index in a list of unique errors rather than // jotting down every single error in its entirety. We'll add one to the result so we can avoid // serializing this property when it has the default value of 0. serializableResult.errorIndex = Array.IndexOf(uniqueErrors, rerunResult.Details) + 1; } - + return serializableResult; } @@ -617,7 +617,7 @@ namespace HelixTestHelpers var filename = Path.GetFileName(filePath); return string.Format("{0}/{1}{2}", helixResultsContainerUri, filename, helixResultsContainerRsas); } - + private string GetTestNameSeparator(string testname) { var separatorString = "."; diff --git a/build/config/esrp.build.batch.wpfdotnet.json b/build/config/esrp.build.batch.wpfdotnet.json index 316aa70116..2f36710deb 100644 --- a/build/config/esrp.build.batch.wpfdotnet.json +++ b/build/config/esrp.build.batch.wpfdotnet.json @@ -2,7 +2,7 @@ { "MatchedPath": [ "WpfTerminalControl/net472/Microsoft.Terminal.Wpf.dll", - "WpfTerminalControl/net6.0-windows/Microsoft.Terminal.Wpf.dll" + "WpfTerminalControl/net8.0-windows/Microsoft.Terminal.Wpf.dll" ], "SigningInfo": { "Operations": [ diff --git a/build/config/template.appinstaller b/build/config/template.appinstaller index 7977fd950b..fe94eed78b 100644 --- a/build/config/template.appinstaller +++ b/build/config/template.appinstaller @@ -14,21 +14,21 @@ + Uri="https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.5/Microsoft.UI.Xaml.2.8.x64.appx" /> + Uri="https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.5/Microsoft.UI.Xaml.2.8.x86.appx" /> + Uri="https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.5/Microsoft.UI.Xaml.2.8.arm64.appx" /> diff --git a/build/pipelines/ob-nightly.yml b/build/pipelines/ob-nightly.yml index 033d075cd6..63eba379d8 100644 --- a/build/pipelines/ob-nightly.yml +++ b/build/pipelines/ob-nightly.yml @@ -50,6 +50,7 @@ extends: parameters: pool: { type: windows } variables: + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: "$(Build.SourcesDirectory)/_none" diff --git a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml index 8c2836c059..2f68ba92c3 100644 --- a/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml +++ b/build/pipelines/templates-v2/job-deploy-to-azure-storage.yml @@ -75,13 +75,30 @@ jobs: } displayName: "Wrangle Unpackaged builds into place, rename" - - task: AzurePowerShell@5 + - task: PowerShell@2 + displayName: Install Azure Modules from custom PowerShell Gallery Repo + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + inputs: + pwsh: false # We are preparing modules for AzureFileCopy, which uses PowerShell 5.1 + targetType: inline + script: |- + $MachineToken = $env:SYSTEM_ACCESSTOKEN | ConvertTo-SecureString -AsPlainText -Force + $Credential = [PSCredential]::new("ONEBRANCH_TOKEN", $MachineToken) + $MachineToken = $null + $Feed = "https://pkgs.dev.azure.com/shine-oss/terminal/_packaging/TerminalDependencies/nuget/v3/index.json" + Register-PSResourceRepository -Name "PSGalleryUpstream" -Uri $Feed -Trusted + Get-PSResourceRepository + + Install-PSResource -Name Az.Accounts, Az.Storage, Az.Network, Az.Resources, Az.Compute -Repository "PSGalleryUpstream" -Credential $Credential + + - task: AzureFileCopy@6 displayName: Publish to Storage Account inputs: + sourcePath: _out/* + Destination: AzureBlob azureSubscription: ${{ parameters.subscription }} - azurePowerShellVersion: LatestVersion - pwsh: true - ScriptType: InlineScript - Inline: |- - $Env:AZCOPY_AUTO_LOGIN_TYPE="PSCRED" - & AzCopy copy "_out\*" "https://${{ parameters.storageAccount }}.blob.core.windows.net/${{ parameters.storageContainer }}/" --content-type application/octet-stream + storage: ${{ parameters.storageAccount }} + ContainerName: ${{ parameters.storageContainer }} + AdditionalArgumentsForBlobCopy: "--content-type application/octet-stream" + diff --git a/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml b/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml index e30e23b88c..513e5c5007 100644 --- a/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml +++ b/build/pipelines/templates-v2/job-pgo-build-nuget-and-publish.yml @@ -30,7 +30,7 @@ jobs: steps: - checkout: self clean: true - # It is important that this be 0, otherwise git will not fetch the branch ref names that the PGO rules require. + # It is important that this be 0; otherwise, git will not fetch the branch ref names that the PGO rules require. fetchDepth: 0 submodules: false persistCredentials: false diff --git a/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml b/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml index a5ed41f737..1b34002c50 100644 --- a/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml +++ b/build/pipelines/templates-v2/job-publish-symbols-using-symbolrequestprod-api.yml @@ -61,7 +61,7 @@ jobs: pwsh: true ScriptType: InlineScript Inline: |- - $AzToken = (Get-AzAccessToken -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token + $AzToken = (Get-AzAccessToken -AsSecureString -ResourceUrl api://30471ccf-0966-45b9-a979-065dbedb24c1).Token | ConvertFrom-SecureString -AsPlainText Write-Host "##vso[task.setvariable variable=SymbolAccessToken;issecret=true]$AzToken" diff --git a/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml b/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml index 0f09271a72..3746402e3e 100644 --- a/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml +++ b/build/pipelines/templates-v2/pipeline-onebranch-full-release-build.yml @@ -88,6 +88,9 @@ extends: enabled: false globalSdl: # https://aka.ms/obpipelines/sdl enableCheckCFlags: false # CheckCFlags is broken and exploding our builds; to remove, :g/BAD-FLAGS/d + isNativeCode: true + prefast: + enabled: true asyncSdl: enabled: true tsaOptionsFile: 'build/config/tsa.json' @@ -115,6 +118,8 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_runDuring: Build + ob_sdl_checkCompliantCompilerWarnings: true ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -149,6 +154,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a C# build job ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -180,6 +186,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -234,6 +241,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -252,6 +260,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(JobOutputDirectory) @@ -277,6 +286,7 @@ extends: variables: ob_sdl_checkcflags_enabled: false # BAD-FLAGS ob_sdl_xfgcheck_enabled: false # BAD-FLAGS + ob_sdl_prefast_enabled: false # This is a collection of powershell scripts ob_git_checkout: false # This job checks itself out ob_git_skip_checkout_none: true ob_outputDirectory: $(Build.ArtifactStagingDirectory) diff --git a/build/scripts/Set-LatestVCToolsVersion.ps1 b/build/scripts/Set-LatestVCToolsVersion.ps1 index 3156fff1bc..4ebf3a3eec 100644 --- a/build/scripts/Set-LatestVCToolsVersion.ps1 +++ b/build/scripts/Set-LatestVCToolsVersion.ps1 @@ -1,8 +1,28 @@ $VSInstances = ([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml)) $VSPackages = $VSInstances.instances.instance.packages.package -$LatestVCPackage = ($VSInstances.instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" }) +$LatestVCPackage = ($VSPackages | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" }) $LatestVCToolsVersion = $LatestVCPackage.version; +$VSRoot = (& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property 'resolvedInstallationPath') +$VCToolsRoot = Join-Path $VSRoot "VC\Tools\MSVC" + +# We have observed a few instances where the VC tools package version actually +# differs from the version on the files themselves. We might as well check +# whether the version we just found _actually exists_ before we use it. +# We'll use whichever highest version exists. +$PackageVCToolPath = Join-Path $VCToolsRoot $LatestVCToolsVersion +If ($Null -Eq (Get-Item $PackageVCToolPath -ErrorAction:Ignore)) { + $VCToolsVersions = Get-ChildItem $VCToolsRoot | ForEach-Object { + [Version]$_.Name + } | Sort -Descending + $LatestActualVCToolsVersion = $VCToolsVersions | Select -First 1 + + If ([Version]$LatestVCToolsVersion -Ne $LatestActualVCToolsVersion) { + Write-Output "VC Tools Mismatch: Directory = $LatestActualVCToolsVersion, Package = $LatestVCToolsVersion" + $LatestVCToolsVersion = $LatestActualVCToolsVersion.ToString(3) + } +} + Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion" Write-Output "Updating VCToolsVersion environment variable for job" Write-Output "##vso[task.setvariable variable=VCToolsVersion]$LatestVCToolsVersion" diff --git a/custom.props b/custom.props index 8e358b3e0b..d3fea955c2 100644 --- a/custom.props +++ b/custom.props @@ -7,5 +7,8 @@ 1 24 Windows Terminal + 1033 + + \xa9 Microsoft Corporation. All rights reserved. diff --git a/doc/Niksa.md b/doc/Niksa.md index ec6af1f146..d9d82fe39d 100644 --- a/doc/Niksa.md +++ b/doc/Niksa.md @@ -15,7 +15,7 @@ This document serves as a storage point for those posts. ## 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. +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. @@ -27,7 +27,7 @@ I would highly recommend that Gulp convert to using PowerShell scripts and that Original Source: https://github.com/microsoft/terminal/issues/217#issuecomment-404240443 -_Addendum_: cmd.exe is the literal embodiment of [xkcd#1172]([url](https://xkcd.com/1172/)). Every change, no matter how small, will break _someone_. +_Addendum_: cmd.exe is the literal embodiment of [xkcd#1172]([url](https://xkcd.com/1172/)). Every change, no matter how small, will break _someone_. ## Why is typing-to-screen performance better than every other app? @@ -37,33 +37,33 @@ Also, I'm happy to discuss this with you until you're utterly sick of reading it 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. +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. +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. +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 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 whereas 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. +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.... +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 ## How are the Windows graphics/messaging stack assembled? -@stakx, I am referring to USER32 and GDI32. +@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. +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. @@ -71,11 +71,11 @@ On top of GDI, I believe there are things like Common Controls or comctl32.dll w 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. +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 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. +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. @@ -83,7 +83,7 @@ Now, if you're on XAML or one of the other Frameworks... all this intricacy is h 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. +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 @@ -94,7 +94,7 @@ Original Source: https://github.com/microsoft/terminal/issues/327#issuecomment-4 ## Output Processing between "Far East" and "Western" -> +> > ``` > if (WI_IsFlagSet(CharType, C1_CNTRL)) > ``` @@ -120,7 +120,7 @@ Note in both of these, there is a little bit of indirection before `MultiByteToW 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. +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. @@ -139,13 +139,13 @@ Original Source: https://github.com/microsoft/terminal/issues/166#issuecomment-5 ## 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. +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 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. +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 every time we make a change. But that's just not how it is today. @@ -189,7 +189,7 @@ _guest speaker @zadjii-msft_ I think there might be a bit of a misunderstanding here - there are two different kinds of applications we're talking about here: * shell applications, like `cmd.exe`, `powershell`, `zsh`, etc. These are text-only applications that emit streams of characters. They don't care at all about how they're eventually rendered to the user. These are also sometimes referred to as "commandline client" applications. -* terminal applications, like the Windows Terminal, gnome-terminal, xterm, iterm2, hyper. These are graphical applications that can be used to render the output of commandline clients. +* terminal applications, like the Windows Terminal, gnome-terminal, xterm, iterm2, hyper. These are graphical applications that can be used to render the output of commandline clients. On Windows, if you just run `cmd.exe` directly, the OS will create an instance of `conhost.exe` as the _terminal_ for `cmd.exe`. The same thing happens for `powershell.exe`, the system will create a new conhost window for any client that's not already connected to a terminal of some sort. This has lead to an enormous amount of confusion for people thinking that a conhost window is actually a "`cmd` window". `cmd` can't have a window, it's just a commandline application. Its window is always some other terminal. diff --git a/doc/WindowsTestPasses.md b/doc/WindowsTestPasses.md index f733f98af5..1689bbb443 100644 --- a/doc/WindowsTestPasses.md +++ b/doc/WindowsTestPasses.md @@ -31,12 +31,12 @@ Prerequisites: 1. Right click the machine name in the `Device Manager` list and choose `Launch T-Shell`. You can also use `Connect via Console` to get a "remote desktop"-like session to the KVM port on the VM. 1. In T-shell, use `testd Microsoft.Console.TestLab.Desktop.testlist` or a command of that format with a different TESTLIST or TESTMD name from our project (see the [UniversalTest.md] documentation). The `testd` utility will automatically resolve the build/branch/flavor information, dig through the build shares for the matching TESTLIST/TESTMD metadata, and attempt to deploy all relevant packages and dependencies on the device. When it's successful, it will move onto running all the tests and giving you the results. On conclusion, the test results should pop up in the web browser or the `Hubble - Log Viewer` tool provided by the Engineering Systems team. -If some of the above things do not work, go to [https://osgwiki.com] and type them into the Search bar. For instance, if T-Shell isn't found or working, you can find out where to get it or download it on `OSGWiki`. The same goes for the other commands besides `testd` to use in T-shell and more information on what `Hubble` or `Nebula` are. +If some of the above things do not work, go to [https://osgwiki.com] and type them into the Search bar. For instance, if T-Shell isn't found or working, you can find out where to get it or download it on `OSGWiki`. The same goes for the other commands besides `testd` to use in T-shell and more information on what `Hubble` or `Nebula` are. Presumably now you have a failure. Or a success. You can attempt to spelunk the logs in `Hubble` and you might come to a conclusion. Or you can move onto debugging directly. -Now that you've relied on `testd` to get everything deployed and orchestrated and run once on the device, you can use `execd` to run things again or to run a smaller subset of things on the remote device through `T-Shell`. +Now that you've relied on `testd` to get everything deployed and orchestrated and run once on the device, you can use `execd` to run things again or to run a smaller subset of things on the remote device through `T-Shell`. -By default, in the `Universal Test` world, everything will be deployed onto the remote machine at `C:\data\test\bin`. In T-Shell, use `cdd C:\data\test\bin` to change to that directory and then `execd te.exe Microsoft.Console.Host.FeatureTests.dll /name:*TestReadFileEcho*` to run just one specific test. Of course you should substitute the file name and test name parameters as makes sense. And of course you can find out more about `cdd` and `execd` on the `T-shell` page of `OSGWiki`. +By default, in the `Universal Test` world, everything will be deployed onto the remote machine at `C:\data\test\bin`. In T-Shell, use `cdd C:\data\test\bin` to change to that directory and then `execd te.exe Microsoft.Console.Host.FeatureTests.dll /name:*TestReadFileEcho*` to run just one specific test. Of course, you should substitute the file name and test name parameters as makes sense. And of course you can find out more about `cdd` and `execd` on the `T-shell` page of `OSGWiki`. Fortunately, running things through `T-shell` in this fashion is exactly the same way that the testlab orchestrates the tests. If you still don't get good data this way, you can use the `Connect via Console` mechanism way above to try to run things under `WinDBG` or the `Visual Studio Remote Debugger` manually on the machine to get them to repro or under the debugger more completely. diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index d60db96aac..275db4fa37 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -32,13 +32,21 @@ ] }, "DynamicProfileSource": { - "enum": [ - "Windows.Terminal.Wsl", - "Windows.Terminal.Azure", - "Windows.Terminal.PowershellCore", - "Windows.Terminal.VisualStudio" - ], - "type": "string" + "type": "string", + "anyOf": [ + { + "type": "string" + }, + { + "enum": [ + "Microsoft.WSL", + "Windows.Terminal.Wsl", + "Windows.Terminal.Azure", + "Windows.Terminal.PowershellCore", + "Windows.Terminal.VisualStudio" + ] + } + ] }, "BellStyle": { "oneOf": [ @@ -2368,6 +2376,16 @@ "description": "When set to true, the terminal will focus the pane on mouse hover.", "type": "boolean" }, + "experimental.scrollToZoom": { + "default": true, + "description": "When set to true, holding the Ctrl key while scrolling will increase or decrease the terminal font size.", + "type": "boolean" + }, + "experimental.scrollToChangeOpacity": { + "default": true, + "description": "When set to true, holding the Ctrl and Shift keys while scrolling will change the window opacity.", + "type": "boolean" + }, "compatibility.allowHeadless": { "default": false, "description": "When set to true, Windows Terminal will run in the background. This allows globalSummon and quakeMode actions to work even when no windows are open.", diff --git a/doc/creating_a_new_project.md b/doc/creating_a_new_project.md index 0f7f3bd880..2e17b097c6 100644 --- a/doc/creating_a_new_project.md +++ b/doc/creating_a_new_project.md @@ -6,7 +6,7 @@ When creating a new DLL, it was really helpful to reference an existing DLL's `. - [ ] Make sure to `` our pre props at the _top_ of the vcxproj, and our post props at the _bottom_ of the vcxproj. ``` - + @@ -35,7 +35,7 @@ DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE - _Note_: If your new library eventually rolls up as a reference to our Centennial Packaging project `CascadiaPackage`, you don't have to worry about manually adding your definitions to the `AppXManifest.xml` because the Centennial Packaging project automatically enumerates the reference tree of WinMDs and stitches that information into the `AppXManifest.xml`. However, if your new project does _not_ ultimately roll up to a packaging project that will automatically put the references into `AppXManifest`, you will have to add them in manually. ### Troubleshooting -- If you hit an error that looks like this: +- If you hit an error that looks like this: ``` X found processing metadata file ..\blah1\Microsoft.UI.Xaml.winmd, type already exists in file ..\blah\NewDLLProject\Microsoft.UI.Xaml.winmd. ``` @@ -51,4 +51,4 @@ DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE - If you hit a `Class not Registered` error, this might be because a class isn't getting registered in the app manifest. You can go check `src/cascadia/CascadiaPackage/bin/x64/Debug/AppX/AppXManifest.xml` to see if there exist entries to the classes of your newly created DLL. If the references aren't there, double check that you've added `` blocks to both `WindowsTerminal.vcxproj` and `TerminalApp.vcxproj`. -- If you hit an extremely vague error along the lines of `Error in the DLL`, and right before that line you notice that your new DLL is loaded and unloaded right after each other, double check that your new DLL's definitions show up in the `AppXManifest.xml` file. If your new DLL is included as a reference to a project that rolls up to `CascadiaPackage`, double check that you've created a `.def` file for the project. Otherwise if your new project _does not_ roll up to a package that populates the `AppXManifest` references for you, you'll have to add those references yourself. +- If you hit an extremely vague error along the lines of `Error in the DLL`, and right before that line you notice that your new DLL is loaded and unloaded right after each other, double check that your new DLL's definitions show up in the `AppXManifest.xml` file. If your new DLL is included as a reference to a project that rolls up to `CascadiaPackage`, double check that you've created a `.def` file for the project. Otherwise, if your new project _does not_ roll up to a package that populates the `AppXManifest` references for you, you'll have to add those references yourself. diff --git a/doc/specs/#1564 - Settings UI/cascading-settings.md b/doc/specs/#1564 - Settings UI/cascading-settings.md index 78d8fd7bbf..0c358f945e 100644 --- a/doc/specs/#1564 - Settings UI/cascading-settings.md +++ b/doc/specs/#1564 - Settings UI/cascading-settings.md @@ -9,7 +9,7 @@ issue id: 1564 ## Abstract -Windows Terminal's settings model adheres to a cascading settings architecture. This allows a settings object to be defined incrementally across multiple layers of declarations. The value for any global setting like `copyOnSelect`, for example, is set to your settings.json value if one is defined, otherwise defaults.json, and otherwise a system set value. Profiles in particular are more complicated in that they must also take into account the values in `profiles.defaults` and dynamic profile generators. +Windows Terminal's settings model adheres to a cascading settings architecture. This allows a settings object to be defined incrementally across multiple layers of declarations. The value for any global setting like `copyOnSelect`, for example, is set to your settings.json value if one is defined; otherwise, defaults.json, and otherwise a system set value. Profiles in particular are more complicated in that they must also take into account the values in `profiles.defaults` and dynamic profile generators. This spec explores how to represent this feature in the Settings UI. diff --git a/doc/specs/#1595 - Suggestions UI/Snippets.md b/doc/specs/#1595 - Suggestions UI/Snippets.md index f80a9cf52f..39cd15de8a 100644 --- a/doc/specs/#1595 - Suggestions UI/Snippets.md +++ b/doc/specs/#1595 - Suggestions UI/Snippets.md @@ -529,7 +529,7 @@ their own workflows. * `--local`: Save to the `.wt.json` in the CWD, if there is one (or create one) * `--parent`: Save to the `.wt.json` in the first ancestor of the CWD, if - there is one. Otherwise create one here. + there is one. Otherwise, create one here. * `--settings`: Manually save to the settings file? * `--profile`: save to this profile???? Not sure if this is actually possible. Maybe with the `WT_SESSION_ID` env var to figure out which profile is in use diff --git a/doc/specs/#1790 - Font features and axes-spec.md b/doc/specs/#1790 - Font features and axes-spec.md index 26cc5fd40a..bc524a4099 100644 --- a/doc/specs/#1790 - Font features and axes-spec.md +++ b/doc/specs/#1790 - Font features and axes-spec.md @@ -23,7 +23,7 @@ In a similar vein, many fonts allow for setting variations on the font along cer ### Font features -It is already possible to pass in a list of [font feature structs](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_font_feature) to DWrite for it to handle. A font feature struct contains only 2 things: +It is already possible to pass in a list of [font feature structs](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_font_feature) to DWrite for it to handle. A font feature struct contains only 2 things: 1. A font feature tag 2. A parameter value @@ -78,7 +78,7 @@ Aside from additional parsing required for the settings file (which inherently o ### Compatibility -Older versions of Windows may not have the DWrite updates that allow for defining font features and axes of variation. We must make sure to fallback to the current implementation in these cases. +Older versions of Windows may not have the DWrite updates that allow for defining font features and axes of variation. We must make sure to fall back to the current implementation in these cases. ### Performance, Power, and Efficiency @@ -102,4 +102,4 @@ We will also need to consider how we want to represent this in the settings UI. [DWRITE_FONT_FEATURE structure](https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_font_feature) -[DWRITE_FONT_AXIS_VALUE structure](https://docs.microsoft.com/en-us/windows/win32/api/dwrite_3/ns-dwrite_3-dwrite_font_axis_value) \ No newline at end of file +[DWRITE_FONT_AXIS_VALUE structure](https://docs.microsoft.com/en-us/windows/win32/api/dwrite_3/ns-dwrite_3-dwrite_font_axis_value) diff --git a/doc/specs/#4066 - Theme-controlled color scheme switch.md b/doc/specs/#4066 - Theme-controlled color scheme switch.md index 9cb41b3571..ed40c9f42d 100644 --- a/doc/specs/#4066 - Theme-controlled color scheme switch.md +++ b/doc/specs/#4066 - Theme-controlled color scheme switch.md @@ -17,7 +17,7 @@ I work remotely as a developer, so I have to spend a lot of hours in front of my Normally I like dark modes in all the programs and apps I use, but when there's too much sunlight, it becomes annoying, and sometimes even painful, to work in dark mode. So, I have all the programs and apps I use (at least, those that can) set to switch their color themes to what the system has. -The company I work for sent me a Macbook Pro, and my personal phone is an Android, both with automatic dark mode at sunset and light mode at sunrise, and in those devices it's been working relatively well. In Windows, as it is known, there's no such feature, so I manually change between dark and light mode when it's needed, and most of the programs and apps I use go along with this change. Windows Terminal, is not one of them. +The company I work for sent me a MacBook Pro, and my personal phone is an Android, both with automatic dark mode at sunset and light mode at sunrise, and in those devices it's been working relatively well. In Windows, as it is known, there's no such feature, so I manually change between dark and light mode when it's needed, and most of the programs and apps I use go along with this change. Windows Terminal, is not one of them. The theme changes just as expected, but in an app like this, this change only affects the top of the window, leaving almost all of the screen at the mercy of what the color scheme is, and it doesn't depend on the theme, which defeats any attempt to make a good use of the `system` theme feature. diff --git a/doc/specs/#5000 - Process Model 2.0/#4472 - Windows Terminal Session Management.md b/doc/specs/#5000 - Process Model 2.0/#4472 - Windows Terminal Session Management.md index 538da23725..625ef51e92 100644 --- a/doc/specs/#5000 - Process Model 2.0/#4472 - Windows Terminal Session Management.md +++ b/doc/specs/#5000 - Process Model 2.0/#4472 - Windows Terminal Session Management.md @@ -94,7 +94,7 @@ configurations: - `"useExisting"`: always glom to the most recent window, regardless of desktop. - `"useExistingOnSameDesktop"`: Only glom if there's an existing window on this - virtual desktop, otherwise create a new window. This will be the new default + virtual desktop; otherwise, create a new window. This will be the new default value. - `"useNew"`: Never glom, always create a new window. This is technically the current behavior of the Terminal. diff --git a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md index 72a672cffa..620e5c30a6 100644 --- a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md +++ b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md @@ -362,7 +362,7 @@ Essentially, the probabilistic elective monarchy will work in the following way: register. 3. After registering as a server for `Monarch`s, attempt to create a `Monarch` using `winrt::create_instance`. -4. Using that `Monarch`, ask it for it's PID. +4. Using that `Monarch`, ask it for its PID. - If that PID is the same as the PID of the current process, then the window process knows that it is the monarch. - If that PID is some other process, then we know that we're not currently @@ -1102,7 +1102,7 @@ launch to use seems like an obvious next step. See also [#961]. - `true` or `"always"`: always glom to the most recent window, regardless of desktop - `"sameDesktop"`: Only glom if there's an existing window on this virtual - desktop, otherwise create a new window + desktop; otherwise, create a new window - `false` or `"never"`: Never glom, always create a new window. diff --git a/doc/specs/#605 - Search/spec.md b/doc/specs/#605 - Search/spec.md index 66a3b6dd54..e828cec0f8 100644 --- a/doc/specs/#605 - Search/spec.md +++ b/doc/specs/#605 - Search/spec.md @@ -13,7 +13,7 @@ This spec is for feature request #605 "Search". It goes over the details of a ne ## 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. +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 a 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 diff --git a/doc/specs/#7335 - Console Allocation Policy.md b/doc/specs/#7335 - Console Allocation Policy.md index c094a8edfc..f05df179d1 100644 --- a/doc/specs/#7335 - Console Allocation Policy.md +++ b/doc/specs/#7335 - Console Allocation Policy.md @@ -331,7 +331,7 @@ Are there other allocation policies we need to consider? - requires coordination between tooling teams both within and without Microsoft (regarding any tool that operates on or produces PE files) -- An exported symbol that shells can check for to determine whether to wait for the attached process to exit +- An exported symbol that shells can check for in order to determine whether to wait for the attached process to exit - relies on shells to update and check for this - cracking an executable to look for symbols is probably the last thing shells want to do - we could provide an API to determine whether to wait or return? diff --git a/doc/specs/#754 - Cascading Default Settings.md b/doc/specs/#754 - Cascading Default Settings.md index 73fd1928dc..5d43c2d0c0 100644 --- a/doc/specs/#754 - Cascading Default Settings.md +++ b/doc/specs/#754 - Cascading Default Settings.md @@ -360,7 +360,7 @@ 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 +The generator does not _need_ to use `GetGuidForName` to generate guids for its 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. diff --git a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md index 5426c9716d..6e27d19238 100644 --- a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md +++ b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md @@ -151,11 +151,11 @@ void CascadiaSettings::LayerJson(const Json::Value& json) // repeat the same for Profiles... } ``` -For `defaults.json`, `_globals` will now hold all of the values set in `defaults.json`. If any settings were omitted from the `defaults.json`, `_globals` will fallback to its parent (a `GlobalAppSettings` consisting purely of system-defined values). +For `defaults.json`, `_globals` will now hold all of the values set in `defaults.json`. If any settings were omitted from the `defaults.json`, `_globals` will fall back to its parent (a `GlobalAppSettings` consisting purely of system-defined values). -For `settings.json`, `_globals` will only hold the values set in `settings.json`. If any settings were omitted from `settings.json`, `_globals` will fallback to its parent (the `GlobalAppSettings` built from `defaults.json`). +For `settings.json`, `_globals` will only hold the values set in `settings.json`. If any settings were omitted from `settings.json`, `_globals` will fall back to its parent (the `GlobalAppSettings` built from `defaults.json`). -This process becomes a bit more complex for `Profile` because it can fallback in the following order: +This process becomes a bit more complex for `Profile` because it can fall back in the following order: 1. `settings.json` profile 2. `settings.json` `profiles.defaults` 3. (if a dynamic profile) the hard-coded value in the dynamic profile generator diff --git a/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md b/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md index c201c0bc33..6bf09a5786 100644 --- a/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md +++ b/doc/specs/drafts/#2634 - Broadcast Input/#2634 - Broadcast Input.md @@ -101,8 +101,8 @@ The scopes would work as follows: this tab. - **TODO!: FOR DISCUSSION**: Should this disable the tab's "broadcastToAllPanes" setting? Or should it leave that alone? -* `"disableBroadcastInput"`: Set the global setting to false, the tab's setting - to false, and clear the set of panes being broadcasted to for this tab. +* `"disableBroadcastInput"`: For this tab, set the global setting to false, + the tab's setting to false, and clear the set of panes being broadcasted. - **TODO!** This could also just be `"action": "toggleBroadcastInput", "scope": "none"` @@ -161,7 +161,7 @@ As far as actions, we're looking at something like: from the broadcast set. Otherwise, add all the panes from this tab to the broadcast set. * **D** toggle sending input to the current pane - * If this pane is in the broadcast set, remove it. Otherwise add it. + * If this pane is in the broadcast set, remove it. Otherwise, add it. This seems to break down into the following actions: ```json diff --git a/doc/terminal-a11y-2023.md b/doc/terminal-a11y-2023.md index 3686120319..60be666fb9 100644 --- a/doc/terminal-a11y-2023.md +++ b/doc/terminal-a11y-2023.md @@ -8,12 +8,12 @@ Since accessibility is a very broad area, this document is intended to present r ### First-party terminals For many years, Console Host (Conhost) was the only first-party terminal on Windows. In 2019, Windows Terminal was released to the world as an open source first-party terminal. Windows Terminal was distributed through the Microsoft Store and received regular updates throughout the year, much more frequently than Conhost. In October 2022, Windows Terminal was enabled as the default terminal on Windows. -A significant amount of code is shared between Conhost and Windows Terminal to create the terminal area. To enable an accessible experience for this area, a shared UI Automation provider was introduced in 2019[^1], enabling accessibility tools to navigate and read contents from the terminal area. In 2020, Windows Terminal was updated to dispatch UIA events signaling when the cursor position, text output, or selection changed; this left the work of identifying what changed in the output to the attached screen reader application[^2]. In 2022, Windows Terminal was updated to dispatch UIA notifications with a payload of what text was written to the screen[^3]. +A significant amount of code is shared between Conhost and Windows Terminal to create the terminal area. To enable an accessible experience for this area, a shared UI Automation provider was introduced in 2019[^1], enabling accessibility tools to navigate and read contents from the terminal area. In 2020, Windows Terminal was updated to dispatch UIA events signaling when the cursor position, text output, or selection changed; this left the work of identifying what changed in the output to the attached screen reader application[^2]. In 2022, Windows Terminal was updated to dispatch UIA notifications with a payload of what text was written to the screen[^3]. ### Internal Partners There are many first-party command-line applications on Windows. The following are a few examples of those that are regularly updated: - [**GitHub CLI**](https://cli.github.com/): a tool that can be used to query and interact with GitHub repos (open source) -- [**Winget**](https://github.com/microsoft/winget-cli): a tool to install applications and other packages +- [**WinGet**](https://github.com/microsoft/winget-cli): a tool to install applications and other packages - [**PSReadLine**](https://github.com/PowerShell/PSReadLine): a PowerShell module that enhances the input line experience - [**Windows Subsystem for Linux (WSL)**](https://learn.microsoft.com/en-us/windows/wsl/): a tool to manage and run GNU/Linux environments without a traditional virtual machine - [**PowerShell**](https://github.com/PowerShell/PowerShell): a cross-platform command-line shell (open source) @@ -33,7 +33,7 @@ The following examples don't take over the entire viewport: - [**Oh My Posh**](https://ohmyposh.dev/): a tool to customize shell prompts - **git**: a tool for version control The following examples operate as command-line shells: -- [**Bash**](https://www.gnu.org/software/bash/) is the default shell for most Linux distributions +- [**Bash**](https://www.gnu.org/software/bash/) is the default shell for most Linux distributions - [**Fish shell**](https://fishshell.com/) provides a rich shell experience with features like autosuggestion support and VGA colors - [**Z shell**](https://zsh.sourceforge.io/) is an extended Bourne shell @@ -113,7 +113,7 @@ This issue is tracked by [megathread: Scrollbar Marks · Issue #11000](https://g ### Mark Mode support for degenerate range [PR #13053](https://github.com/microsoft/terminal/pull/13053) added support for mark mode in Windows Terminal. Mark mode allows users to create and modify selections by exclusively using the keyboard. However, screen reader users have reported it as a strange experience because it always has a cell of text selected; this results in the screen reader reading "x selected, y unselected" as opposed to the expected "x" when moving the cursor around. -Unfortunately, the changes required to fix this are very extensive because selections are stored as two inclusive terminal coordinates, which makes it impossible to represent an empty selection. +Unfortunately, the changes required to fix this are very extensive because selections are stored as two inclusive terminal coordinates, which makes it impossible to represent an empty selection. This is tracked by [A11y: windows terminal emits selection/deselection events in mark mode when navigating with arrow keys · Issue #13447](https://github.com/microsoft/terminal/issues/13447). @@ -158,7 +158,7 @@ In 2022, Windows Terminal added UI Automation notifications that contained a pay UIA notifications have provided many compatibility benefits since screen readers automatically read notifications they receive. Additionally, this has provided the possibility for major performance enhancements as screen readers may no longer be required to diff the text buffer and figure out what has changed. NVDA has prototyped listening to notifications and ignoring text changed events entirely[^7]. However, it reveals underlying challenges with this new model such as how to handle passwords. The proposals listed in this section are intended to have Windows Terminal achieve improved performance and accessibility quality. #### VT Screen Reader Control -Some command-line applications are simply too difficult to create a consistent accessible experience. Applications that draw decorative content, for example, may have that content read by a screen reader. +Some command-line applications are simply too difficult to create a consistent accessible experience. Applications that draw decorative content, for example, may have that content read by a screen reader. In 2019, Daniel Imms wrote a spec proposing a VT sequence that can partially control the attached screen reader[^8]. This VT sequence consists of three main formats: 1. Stop announcing incoming data to the screen reader. The screen reader will resume announcing incoming data if any key is pressed. @@ -214,4 +214,4 @@ Generally, the reasoning behind these priorities can be broken down as follows: [^5]: [Implement the Delta E algorithm to improve color perception by PankajBhojwani · Pull Request #11095](https://github.com/microsoft/terminal/pull/11095) [^6]: [Change AdjustIndistinguishableColors to an enum setting instead of a boolean setting by PankajBhojwani · Pull Request #13512](https://github.com/microsoft/terminal/pull/13512) [^7]: [Prototype for Windows Terminal: Use notifications instead of monitoring for new text by leonardder · Pull Request #14047 · nvaccess/nvda (github.com)](https://github.com/nvaccess/nvda/pull/14047) -[^8]: [Control Screen Reader from Applications (#18) · Issues · terminal-wg / specifications · GitLab](https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/18) \ No newline at end of file +[^8]: [Control Screen Reader from Applications (#18) · Issues · terminal-wg / specifications · GitLab](https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/18) diff --git a/policies/en-US/WindowsTerminal.adml b/policies/en-US/WindowsTerminal.adml index f2bcf71d3c..b0be1c3d57 100644 --- a/policies/en-US/WindowsTerminal.adml +++ b/policies/en-US/WindowsTerminal.adml @@ -22,7 +22,7 @@ Note: Existing profiles will disappear from Windows Terminal after adding their Default terminal application Select the default terminal application used in Windows. -If you select Windows Terminal Preview and it is not installed the system will fallback to the legacy Windows Console Host. (Please note that the settings interfaces showing "Let windows decide" in this case as configuration.) +If you select Windows Terminal Preview and it is not installed the system will fall back to the legacy Windows Console Host. (Please note that the settings interfaces showing "Let windows decide" in this case as configuration.) Automatic selection (Windows Terminal, if available) Windows Console Host (legacy) Windows Terminal diff --git a/samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml b/samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml index 2c67f1cb0e..36cc20b91f 100644 --- a/samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml +++ b/samples/ConPTY/GUIConsole/GUIConsole.WPF/MainWindow.xaml @@ -32,7 +32,7 @@ BasedOn="{StaticResource TitleBarButtonStyle}" TargetType="{x:Type Button}"> - + diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 11f48ec0f6..6f0336e8bc 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2969,7 +2969,7 @@ void TextBuffer::_SerializeRow(const ROW& row, const til::CoordType startX, cons // parameter and we'll calculate the position of the _end_ of those rows in // the new buffer. The rows's new value is placed back into this parameter. // Return Value: -// - S_OK if we successfully copied the contents to the new buffer, otherwise an appropriate HRESULT. +// - S_OK if we successfully copied the contents to the new buffer; otherwise, an appropriate HRESULT. void TextBuffer::Reflow(TextBuffer& oldBuffer, TextBuffer& newBuffer, const Viewport* lastCharacterViewport, PositionInformation* positionInfo) { const auto& oldCursor = oldBuffer.GetCursor(); diff --git a/src/buffer/out/ut_textbuffer/ReflowTests.cpp b/src/buffer/out/ut_textbuffer/ReflowTests.cpp index e46e415e37..58e7766c89 100644 --- a/src/buffer/out/ut_textbuffer/ReflowTests.cpp +++ b/src/buffer/out/ut_textbuffer/ReflowTests.cpp @@ -573,7 +573,7 @@ namespace TestCase{ // This triggers the cursor being walked forward w/ newlines to maintain // distance from the last char in the buffer - L"SBCS, cursor at end of buffer, otherwise same as previous test", + L"SBCS, cursor at end of buffer; otherwise, same as previous test", { TestBuffer{ { 6, 5 }, diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-100.png b/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-100.png deleted file mode 100644 index 6d57b166f2..0000000000 Binary files a/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-100.png and /dev/null differ diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-200.png b/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-200.png deleted file mode 100644 index ebba22951f..0000000000 Binary files a/src/cascadia/CascadiaPackage/ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.scale-200.png and /dev/null differ diff --git a/src/cascadia/ElevateShim/elevate-shim.vcxproj b/src/cascadia/ElevateShim/elevate-shim.vcxproj index 95b6240659..80dccc6b0c 100644 --- a/src/cascadia/ElevateShim/elevate-shim.vcxproj +++ b/src/cascadia/ElevateShim/elevate-shim.vcxproj @@ -7,6 +7,7 @@ elevate-shim elevate-shim Application + Windows Terminal Administrator Launch Helper diff --git a/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp b/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp index e96675fd32..c079a547d1 100644 --- a/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp @@ -2,8 +2,8 @@ // Licensed under the MIT license. #include "pch.h" -#include "../TerminalApp/CommandLinePaletteItem.h" #include "../TerminalApp/CommandPalette.h" +#include "../TerminalApp/BasePaletteItem.h" #include "CppWinrtTailored.h" using namespace Microsoft::Console; @@ -15,6 +15,20 @@ using namespace winrt::Microsoft::Terminal::Control; namespace TerminalAppLocalTests { + struct StringPaletteItem : winrt::implements, winrt::TerminalApp::implementation::BasePaletteItem + { + StringPaletteItem(std::wstring_view value) : + _value{ value } {} + + winrt::hstring Name() { return _value; } + winrt::hstring Subtitle() { return {}; } + winrt::hstring KeyChordText() { return {}; } + winrt::hstring Icon() { return {}; } + + private: + winrt::hstring _value; + }; + class FilteredCommandTests { BEGIN_TEST_CLASS(FilteredCommandTests) @@ -28,81 +42,81 @@ namespace TerminalAppLocalTests TEST_METHOD(VerifyCompareIgnoreCase); }; + static void _verifySegment(auto&& segments, uint32_t index, uint64_t start, uint64_t end) + { + const auto& segment{ segments.GetAt(index) }; + VERIFY_ARE_EQUAL(segment.Start, start, NoThrowString().Format(L"segment %zu", index)); + VERIFY_ARE_EQUAL(segment.End, end, NoThrowString().Format(L"segment %zu", index)); + } + void FilteredCommandTests::VerifyHighlighting() { auto result = RunOnUIThread([]() { - const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; + const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope; + + const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; + const auto filteredCommand = winrt::make_self(paletteItem); + { Log::Comment(L"Testing command name segmentation with no filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 1u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted()); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_IS_NULL(segments); // No matches = no segments } { Log::Comment(L"Testing command name segmentation with empty filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L""; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 1u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L""))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_IS_NULL(segments); // No matches = no segments } { Log::Comment(L"Testing command name segmentation with filter equal to the string"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"AAAAAABBBBBBCCC"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"AAAAAABBBBBBCCC"))); + const auto segments = filteredCommand->NameHighlights(); + VERIFY_ARE_EQUAL(segments.Size(), 1u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC"); - VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted()); + _verifySegment(segments, 0, 0, 14); // one segment for the entire string } { Log::Comment(L"Testing command name segmentation with filter with first character matching"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"A"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 2u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A"); - VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"A"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_ARE_EQUAL(segments.Size(), 1u); // only one bold segment + _verifySegment(segments, 0, 0, 0); // it only covers the first character } { Log::Comment(L"Testing command name segmentation with filter with other case"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"a"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 2u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A"); - VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"a"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_ARE_EQUAL(segments.Size(), 1u); // only one bold segment + _verifySegment(segments, 0, 0, 0); // it only covers the first character } { Log::Comment(L"Testing command name segmentation with filter matching several characters"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"ab"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 4u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A"); - VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAA"); - VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(2).TextSegment(), L"B"); - VERIFY_IS_TRUE(segments.GetAt(2).IsHighlighted()); - VERIFY_ARE_EQUAL(segments.GetAt(3).TextSegment(), L"BBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(3).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"ab"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_ARE_EQUAL(segments.Size(), 1u); // one bold segment + _verifySegment(segments, 0, 5, 6); // middle 'ab' + } + { + Log::Comment(L"Testing command name segmentation with filter matching several regions"); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"abcc"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_ARE_EQUAL(segments.Size(), 2u); // two bold segments + _verifySegment(segments, 0, 5, 6); // middle 'ab' + _verifySegment(segments, 1, 12, 13); // start of 'cc' } { Log::Comment(L"Testing command name segmentation with non matching filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"abcd"; - auto segments = filteredCommand->_computeHighlightedName().Segments(); - VERIFY_ARE_EQUAL(segments.Size(), 1u); - VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC"); - VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted()); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"abcd"))); + const auto segments = filteredCommand->NameHighlights(); + + VERIFY_IS_NULL(segments); // No matches = no segments } }); @@ -112,54 +126,38 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyWeight() { auto result = RunOnUIThread([]() { - const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; - { - Log::Comment(L"Testing weight of command with no filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 0); - } - { - Log::Comment(L"Testing weight of command with empty filter"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L""; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 0); - } - { - Log::Comment(L"Testing weight of command with filter equal to the string"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"AAAAAABBBBBBCCC"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 30); // 1 point for the first char and 2 points for the 14 consequent ones + 1 point for the beginning of the word - } - { - Log::Comment(L"Testing weight of command with filter with first character matching"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"A"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word - } - { - Log::Comment(L"Testing weight of command with filter with other case"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"a"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word - } - { - Log::Comment(L"Testing weight of command with filter matching several characters"); - const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"ab"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - auto weight = filteredCommand->_computeWeight(); - VERIFY_ARE_EQUAL(weight, 3); // 1 point for the first char match + 1 point for the beginning of the word + 1 point for the match of "b" - } + const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; + const auto filteredCommand = winrt::make_self(paletteItem); + + const auto weigh = [&](const wchar_t* str) { + std::shared_ptr pattern; + if (str) + { + pattern = std::make_shared(fzf::matcher::ParsePattern(str)); + } + filteredCommand->UpdateFilter(std::move(pattern)); + return filteredCommand->Weight(); + }; + + const auto null = weigh(nullptr); + const auto empty = weigh(L""); + const auto full = weigh(L"AAAAAABBBBBBCCC"); + const auto firstChar = weigh(L"A"); + const auto otherCase = weigh(L"a"); + const auto severalChars = weigh(L"ab"); + + VERIFY_ARE_EQUAL(null, 0); + VERIFY_ARE_EQUAL(empty, 0); + VERIFY_IS_GREATER_THAN(full, 100); + + VERIFY_IS_GREATER_THAN(firstChar, 0); + VERIFY_IS_LESS_THAN(firstChar, full); + + VERIFY_IS_GREATER_THAN(otherCase, 0); + VERIFY_IS_LESS_THAN(otherCase, full); + + VERIFY_IS_GREATER_THAN(severalChars, otherCase); + VERIFY_IS_LESS_THAN(severalChars, full); }); VERIFY_SUCCEEDED(result); @@ -168,8 +166,8 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyCompare() { auto result = RunOnUIThread([]() { - const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; - const auto paletteItem2{ winrt::make(L"BBBBBCCC") }; + const auto paletteItem{ winrt::make(L"AAAAAABBBBBBCCC") }; + const auto paletteItem2{ winrt::make(L"BBBBBCCC") }; { Log::Comment(L"Testing comparison of commands with no filter"); const auto filteredCommand = winrt::make_self(paletteItem); @@ -181,14 +179,10 @@ namespace TerminalAppLocalTests { Log::Comment(L"Testing comparison of commands with empty filter"); const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L""; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - filteredCommand->_Weight = filteredCommand->_computeWeight(); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L""))); const auto filteredCommand2 = winrt::make_self(paletteItem2); - filteredCommand2->_Filter = L""; - filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName(); - filteredCommand2->_Weight = filteredCommand2->_computeWeight(); + filteredCommand2->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L""))); VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight()); VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2)); @@ -196,16 +190,12 @@ namespace TerminalAppLocalTests { Log::Comment(L"Testing comparison of commands with different weights"); const auto filteredCommand = winrt::make_self(paletteItem); - filteredCommand->_Filter = L"B"; - filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName(); - filteredCommand->_Weight = filteredCommand->_computeWeight(); + filteredCommand->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"B"))); const auto filteredCommand2 = winrt::make_self(paletteItem2); - filteredCommand2->_Filter = L"B"; - filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName(); - filteredCommand2->_Weight = filteredCommand2->_computeWeight(); + filteredCommand2->UpdateFilter(std::make_shared(fzf::matcher::ParsePattern(L"B"))); - VERIFY_IS_TRUE(filteredCommand->Weight() < filteredCommand2->Weight()); // Second command gets more points due to the beginning of the word + VERIFY_IS_LESS_THAN(filteredCommand->Weight(), filteredCommand2->Weight()); // Second command gets more points due to the beginning of the word VERIFY_IS_FALSE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2)); } }); @@ -216,8 +206,8 @@ namespace TerminalAppLocalTests void FilteredCommandTests::VerifyCompareIgnoreCase() { auto result = RunOnUIThread([]() { - const auto paletteItem{ winrt::make(L"a") }; - const auto paletteItem2{ winrt::make(L"B") }; + const auto paletteItem{ winrt::make(L"a") }; + const auto paletteItem2{ winrt::make(L"B") }; { const auto filteredCommand = winrt::make_self(paletteItem); const auto filteredCommand2 = winrt::make_self(paletteItem2); diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index 941d5fa06f..4e1744c66c 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -8,7 +8,7 @@ #include "../TerminalApp/MinMaxCloseControl.h" #include "../TerminalApp/TabRowControl.h" #include "../TerminalApp/ShortcutActionDispatch.h" -#include "../TerminalApp/TerminalTab.h" +#include "../TerminalApp/Tab.h" #include "../TerminalApp/CommandPalette.h" #include "../TerminalApp/ContentManager.h" #include "CppWinrtTailored.h" @@ -307,7 +307,7 @@ namespace TerminalAppLocalTests // reliably in the unit tests. Log::Comment(L"Ensure we set the first tab as the selected one."); auto tab = page->_tabs.GetAt(0); - auto tabImpl = page->_GetTerminalTabImpl(tab); + auto tabImpl = page->_GetTabImpl(tab); page->_tabView.SelectedItem(tabImpl->TabViewItem()); page->_UpdatedSelectedTab(tab); }); @@ -510,7 +510,7 @@ namespace TerminalAppLocalTests result = RunOnUIThread([&page]() { VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(1, tab->GetLeafPaneCount()); }); VERIFY_SUCCEEDED(result); @@ -520,7 +520,7 @@ namespace TerminalAppLocalTests page->_SplitPane(nullptr, SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr)); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, tab->GetLeafPaneCount()); }); VERIFY_SUCCEEDED(result); @@ -538,7 +538,7 @@ namespace TerminalAppLocalTests page->_SplitPane(nullptr, SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr)); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(3, tab->GetLeafPaneCount(), L"We should successfully duplicate a pane hosting a deleted profile."); @@ -706,7 +706,7 @@ namespace TerminalAppLocalTests SplitPaneArgs args{ SplitType::Duplicate }; ActionEventArgs eventArgs{ args }; page->_HandleSplitPane(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); @@ -717,7 +717,7 @@ namespace TerminalAppLocalTests result = RunOnUIThread([&page]() { ActionEventArgs eventArgs{}; page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -727,7 +727,7 @@ namespace TerminalAppLocalTests result = RunOnUIThread([&page]() { ActionEventArgs eventArgs{}; page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); @@ -744,7 +744,7 @@ namespace TerminalAppLocalTests SplitPaneArgs args{ SplitType::Duplicate }; ActionEventArgs eventArgs{ args }; page->_HandleSplitPane(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); @@ -758,7 +758,7 @@ namespace TerminalAppLocalTests page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -772,7 +772,7 @@ namespace TerminalAppLocalTests page->_HandleMoveFocus(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -789,7 +789,7 @@ namespace TerminalAppLocalTests SplitPaneArgs args{ SplitType::Duplicate }; ActionEventArgs eventArgs{ args }; page->_HandleSplitPane(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); @@ -803,7 +803,7 @@ namespace TerminalAppLocalTests page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -816,7 +816,7 @@ namespace TerminalAppLocalTests page->_HandleClosePane(nullptr, eventArgs); - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); VERIFY_SUCCEEDED(result); @@ -827,7 +827,7 @@ namespace TerminalAppLocalTests Log::Comment(L"Check to ensure there's only one pane left."); result = RunOnUIThread([&page]() { - auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto firstTab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(1, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); @@ -850,7 +850,7 @@ namespace TerminalAppLocalTests uint32_t firstId = 0, secondId = 0, thirdId = 0, fourthId = 0; TestOnUIThread([&]() { VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); firstId = tab->_activePane->Id().value(); // We start with 1 tab, split vertically to get // ------------------- @@ -876,7 +876,7 @@ namespace TerminalAppLocalTests // | | | // ------------------- page->_SplitPane(nullptr, SplitDirection::Down, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr)); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); // Split again to make the 3rd tab thirdId = tab->_activePane->Id().value(); }); @@ -896,13 +896,13 @@ namespace TerminalAppLocalTests // | | | // ------------------- page->_SplitPane(nullptr, SplitDirection::Down, 0.5f, page->_MakePane(nullptr, page->_GetFocusedTab(), nullptr)); - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); fourthId = tab->_activePane->Id().value(); }); Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // just to be complete, make sure we actually have 4 different ids VERIFY_ARE_NOT_EQUAL(firstId, fourthId); @@ -936,7 +936,7 @@ namespace TerminalAppLocalTests Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // Our currently focused pane should be `4` VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value()); @@ -967,7 +967,7 @@ namespace TerminalAppLocalTests Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // Our currently focused pane should be `4` VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value()); @@ -998,7 +998,7 @@ namespace TerminalAppLocalTests Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // Our currently focused pane should be `4` VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value()); @@ -1029,7 +1029,7 @@ namespace TerminalAppLocalTests Sleep(250); TestOnUIThread([&]() { - auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + auto tab = page->_GetTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(4, tab->GetLeafPaneCount()); // Our currently focused pane should be `4` VERIFY_ARE_EQUAL(fourthId, tab->_activePane->Id().value()); @@ -1174,16 +1174,16 @@ namespace TerminalAppLocalTests Log::Comment(L"give alphabetical names to all switch tab actions"); TestOnUIThread([&page]() { - page->_GetTerminalTabImpl(page->_tabs.GetAt(0))->Title(L"a"); + page->_GetTabImpl(page->_tabs.GetAt(0))->Title(L"a"); }); TestOnUIThread([&page]() { - page->_GetTerminalTabImpl(page->_tabs.GetAt(1))->Title(L"b"); + page->_GetTabImpl(page->_tabs.GetAt(1))->Title(L"b"); }); TestOnUIThread([&page]() { - page->_GetTerminalTabImpl(page->_tabs.GetAt(2))->Title(L"c"); + page->_GetTabImpl(page->_tabs.GetAt(2))->Title(L"c"); }); TestOnUIThread([&page]() { - page->_GetTerminalTabImpl(page->_tabs.GetAt(3))->Title(L"d"); + page->_GetTabImpl(page->_tabs.GetAt(3))->Title(L"d"); }); TestOnUIThread([&page]() { diff --git a/src/cascadia/ShellExtension/OpenTerminalHere.cpp b/src/cascadia/ShellExtension/OpenTerminalHere.cpp index 88d4f43551..e3d0f813f6 100644 --- a/src/cascadia/ShellExtension/OpenTerminalHere.cpp +++ b/src/cascadia/ShellExtension/OpenTerminalHere.cpp @@ -21,7 +21,7 @@ static constexpr std::wstring_view VerbName{ L"WindowsTerminalOpenHere" }; // Arguments: // - psiItemArray: a IShellItemArray which contains the item that's selected. // Return Value: -// - S_OK if we successfully attempted to launch the Terminal, otherwise a +// - S_OK if we successfully attempted to launch the Terminal; otherwise, a // failure from an earlier HRESULT. HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray, IBindCtx* /*pBindContext*/) diff --git a/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj b/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj index 4207b58282..d4cc256839 100644 --- a/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj +++ b/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj @@ -10,6 +10,7 @@ Console false + Windows Terminal Open Here Shell Extension true diff --git a/src/cascadia/TerminalApp/AboutDialog.xaml b/src/cascadia/TerminalApp/AboutDialog.xaml index b9aebf9a9b..ff075efb9a 100644 --- a/src/cascadia/TerminalApp/AboutDialog.xaml +++ b/src/cascadia/TerminalApp/AboutDialog.xaml @@ -28,18 +28,18 @@ - - + - @@ -51,15 +51,18 @@ - - - - - + + + + + + + diff --git a/src/cascadia/TerminalApp/ActionPaletteItem.cpp b/src/cascadia/TerminalApp/ActionPaletteItem.cpp deleted file mode 100644 index 0c4a439b81..0000000000 --- a/src/cascadia/TerminalApp/ActionPaletteItem.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "ActionPaletteItem.h" -#include - -#include "ActionPaletteItem.g.cpp" - -using namespace winrt; -using namespace winrt::TerminalApp; -using namespace winrt::Windows::UI::Core; -using namespace winrt::Windows::UI::Xaml; -using namespace winrt::Windows::System; -using namespace winrt::Windows::Foundation; -using namespace winrt::Windows::Foundation::Collections; -using namespace winrt::Microsoft::Terminal::Settings::Model; - -namespace winrt::TerminalApp::implementation -{ - ActionPaletteItem::ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command, const winrt::hstring keyChordText) : - _Command(command) - { - Name(command.Name()); - KeyChordText(keyChordText); - Icon(command.IconPath()); - } -} diff --git a/src/cascadia/TerminalApp/ActionPaletteItem.h b/src/cascadia/TerminalApp/ActionPaletteItem.h deleted file mode 100644 index 6679c38086..0000000000 --- a/src/cascadia/TerminalApp/ActionPaletteItem.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once - -#include "PaletteItem.h" -#include "ActionPaletteItem.g.h" - -namespace winrt::TerminalApp::implementation -{ - struct ActionPaletteItem : ActionPaletteItemT - { - ActionPaletteItem() = default; - ActionPaletteItem(const Microsoft::Terminal::Settings::Model::Command& command, const winrt::hstring keyChordText); - - WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr); - - private: - Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _commandChangedRevoker; - }; -} - -namespace winrt::TerminalApp::factory_implementation -{ - BASIC_FACTORY(ActionPaletteItem); -} diff --git a/src/cascadia/TerminalApp/ActionPaletteItem.idl b/src/cascadia/TerminalApp/ActionPaletteItem.idl deleted file mode 100644 index f272c7eb00..0000000000 --- a/src/cascadia/TerminalApp/ActionPaletteItem.idl +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import "PaletteItem.idl"; - -namespace TerminalApp -{ - [default_interface] runtimeclass ActionPaletteItem : PaletteItem - { - ActionPaletteItem(Microsoft.Terminal.Settings.Model.Command command, String keyChordText); - - Microsoft.Terminal.Settings.Model.Command Command { get; }; - } -} diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml index cc0ce1517d..4210b65b0a 100644 --- a/src/cascadia/TerminalApp/App.xaml +++ b/src/cascadia/TerminalApp/App.xaml @@ -243,6 +243,8 @@ + + diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 6964892963..072f28c843 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -41,13 +41,13 @@ namespace winrt::TerminalApp::implementation } return _GetActiveControl(); } - winrt::com_ptr TerminalPage::_senderOrFocusedTab(const IInspectable& sender) + winrt::com_ptr TerminalPage::_senderOrFocusedTab(const IInspectable& sender) { if (sender) { - if (auto tab{ sender.try_as() }) + if (auto tab = sender.try_as()) { - return _GetTerminalTabImpl(tab); + return _GetTabImpl(tab); } } return _GetFocusedTabImpl(); @@ -193,17 +193,17 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleCloseOtherPanes(const IInspectable& sender, const ActionEventArgs& args) { - if (const auto& terminalTab{ _senderOrFocusedTab(sender) }) + if (const auto& activeTab{ _senderOrFocusedTab(sender) }) { - const auto activePane = terminalTab->GetActivePane(); - if (terminalTab->GetRootPane() != activePane) + const auto activePane = activeTab->GetActivePane(); + if (activeTab->GetRootPane() != activePane) { _UnZoomIfNeeded(); // Accumulate list of all unfocused leaf panes, ignore read-only panes std::vector unfocusedPaneIds; const auto activePaneId = activePane->Id(); - terminalTab->GetRootPane()->WalkTree([&](auto&& p) { + activeTab->GetRootPane()->WalkTree([&](auto&& p) { const auto id = p->Id(); if (id.has_value() && id != activePaneId && !p->ContainsReadOnly()) { @@ -215,7 +215,7 @@ namespace winrt::TerminalApp::implementation { // Start by removing the panes that were least recently added sort(begin(unfocusedPaneIds), end(unfocusedPaneIds), std::less()); - _ClosePanes(terminalTab->get_weak(), std::move(unfocusedPaneIds)); + _ClosePanes(activeTab->get_weak(), std::move(unfocusedPaneIds)); args.Handled(true); return; } @@ -281,9 +281,9 @@ namespace winrt::TerminalApp::implementation const auto& duplicateFromTab{ realArgs.SplitMode() == SplitType::Duplicate ? _GetFocusedTab() : nullptr }; - const auto& terminalTab{ _senderOrFocusedTab(sender) }; + const auto& activeTab{ _senderOrFocusedTab(sender) }; - _SplitPane(terminalTab, + _SplitPane(activeTab, realArgs.SplitDirection(), // This is safe, we're already filtering so the value is (0, 1) realArgs.SplitSize(), @@ -302,14 +302,14 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleTogglePaneZoom(const IInspectable& sender, const ActionEventArgs& args) { - if (const auto terminalTab{ _senderOrFocusedTab(sender) }) + if (const auto activeTab{ _senderOrFocusedTab(sender) }) { // Don't do anything if there's only one pane. It's already zoomed. - if (terminalTab->GetLeafPaneCount() > 1) + if (activeTab->GetLeafPaneCount() > 1) { // Togging the zoom on the tab will cause the tab to inform us of // the new root Content for this tab. - terminalTab->ToggleZoom(); + activeTab->ToggleZoom(); } } @@ -517,7 +517,7 @@ namespace winrt::TerminalApp::implementation else { // Mark as handled only when the move succeeded (e.g. when there - // is a pane to move to), otherwise mark as unhandled so the + // is a pane to move to); otherwise, mark as unhandled so the // keychord can propagate to the terminal (GH#6129) const auto moveSucceeded = _MoveFocus(realArgs.FocusDirection()); args.Handled(moveSucceeded); @@ -548,7 +548,9 @@ namespace winrt::TerminalApp::implementation { if (const auto& realArgs = args.ActionArgs().try_as()) { - const auto handled = _CopyText(realArgs.DismissSelection(), realArgs.SingleLine(), realArgs.WithControlSequences(), realArgs.CopyFormatting()); + const auto copyFormatting = realArgs.CopyFormatting(); + const auto format = copyFormatting ? copyFormatting.Value() : _settings.GlobalSettings().CopyFormatting(); + const auto handled = _CopyText(realArgs.DismissSelection(), realArgs.SingleLine(), realArgs.WithControlSequences(), format); args.Handled(handled); } } @@ -756,7 +758,7 @@ namespace winrt::TerminalApp::implementation if (!actions.empty()) { actionArgs.Handled(true); - ProcessStartupActions(std::move(actions), false); + ProcessStartupActions(std::move(actions)); } } } @@ -783,7 +785,7 @@ namespace winrt::TerminalApp::implementation } // Since _RemoveTabs is asynchronous, create a snapshot of the tabs we want to remove - std::vector tabsToRemove; + std::vector tabsToRemove; if (index > 0) { std::copy(begin(_tabs), begin(_tabs) + index, std::back_inserter(tabsToRemove)); @@ -822,7 +824,7 @@ namespace winrt::TerminalApp::implementation } // Since _RemoveTabs is asynchronous, create a snapshot of the tabs we want to remove - std::vector tabsToRemove; + std::vector tabsToRemove; std::copy(begin(_tabs) + index + 1, end(_tabs), std::back_inserter(tabsToRemove)); _RemoveTabs(tabsToRemove); @@ -1559,7 +1561,6 @@ namespace winrt::TerminalApp::implementation activeTab->ToggleBroadcastInput(); args.Handled(true); } - // If the focused tab wasn't a TerminalTab, then leave handled=false } void TerminalPage::_HandleRestartConnection(const IInspectable& sender, diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 1e4b7a3b10..1b7881dc27 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -961,18 +961,6 @@ std::vector& AppCommandlineArgs::GetStartupActions() return _startupActions; } -// Method Description: -// - Returns whether we should start listening for inbound PTY connections -// coming from the operating system default application feature. -// Arguments: -// - -// Return Value: -// - True if the listener should be started. False otherwise. -bool AppCommandlineArgs::IsHandoffListener() const noexcept -{ - return _isHandoffListener; -} - // Method Description: // - Get the string of text that should be displayed to the user on exit. This // is usually helpful for cases where the user entered some sort of invalid @@ -1015,34 +1003,28 @@ bool AppCommandlineArgs::ShouldExitEarly() const noexcept // - void AppCommandlineArgs::ValidateStartupCommands() { - // Only check over the actions list for the potential to add a new-tab - // command if we are not starting for the purposes of receiving an inbound - // handoff connection from the operating system. - if (!_isHandoffListener) + // If we only have a single x-save command, then set our target to the + // current terminal window. This will prevent us from spawning a new + // window just to save the commandline. + if (_startupActions.size() == 1 && + _startupActions.front().Action() == ShortcutAction::SaveSnippet && + _windowTarget.empty()) { - // If we only have a single x-save command, then set our target to the - // current terminal window. This will prevent us from spawning a new - // window just to save the commandline. - if (_startupActions.size() == 1 && - _startupActions.front().Action() == ShortcutAction::SaveSnippet && - _windowTarget.empty()) - { - _windowTarget = "0"; - } - // If we parsed no commands, or the first command we've parsed is not a new - // tab action, prepend a new-tab command to the front of the list. - // (also, we don't need to do this if the only action is a x-save) - else if (_startupActions.empty() || - (_startupActions.front().Action() != ShortcutAction::NewTab && - _startupActions.front().Action() != ShortcutAction::SaveSnippet)) - { - // Build the NewTab action from the values we've parsed on the commandline. - NewTerminalArgs newTerminalArgs{}; - NewTabArgs args{ newTerminalArgs }; - ActionAndArgs newTabAction{ ShortcutAction::NewTab, args }; - // push the arg onto the front - _startupActions.insert(_startupActions.begin(), 1, newTabAction); - } + _windowTarget = "0"; + } + // If we parsed no commands, or the first command we've parsed is not a new + // tab action, prepend a new-tab command to the front of the list. + // (also, we don't need to do this if the only action is a x-save) + else if (_startupActions.empty() || + (_startupActions.front().Action() != ShortcutAction::NewTab && + _startupActions.front().Action() != ShortcutAction::SaveSnippet)) + { + // Build the NewTab action from the values we've parsed on the commandline. + NewTerminalArgs newTerminalArgs{}; + NewTabArgs args{ newTerminalArgs }; + ActionAndArgs newTabAction{ ShortcutAction::NewTab, args }; + // push the arg onto the front + _startupActions.insert(_startupActions.begin(), 1, newTabAction); } } std::optional AppCommandlineArgs::GetPersistedLayoutIdx() const noexcept @@ -1082,13 +1064,9 @@ std::optional AppCommandlineArgs::GetSize() const noexcept // - 0 if the commandline was successfully parsed int AppCommandlineArgs::ParseArgs(winrt::array_view args) { - for (const auto& arg : args) + if (args.size() == 2 && args[1] == L"-Embedding") { - if (arg == L"-Embedding") - { - _isHandoffListener = true; - return 0; - } + return 0; } auto commands = ::TerminalApp::AppCommandlineArgs::BuildCommands(args); @@ -1195,7 +1173,6 @@ void AppCommandlineArgs::FullResetState() _startupActions.clear(); _exitMessage = ""; _shouldExitEarly = false; - _isHandoffListener = false; _windowTarget = {}; } diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h index 9f580e957b..0a9de3999c 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.h +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h @@ -35,7 +35,6 @@ public: void ValidateStartupCommands(); std::vector& GetStartupActions(); - bool IsHandoffListener() const noexcept; const std::string& GetExitMessage() const noexcept; bool ShouldExitEarly() const noexcept; @@ -132,7 +131,6 @@ private: std::optional _launchMode{ std::nullopt }; std::optional _position{ std::nullopt }; std::optional _size{ std::nullopt }; - bool _isHandoffListener{ false }; std::vector _startupActions; std::string _exitMessage; bool _shouldExitEarly{ false }; diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index b7e0eae91a..0563adbd7a 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -135,12 +135,19 @@ namespace winrt::TerminalApp::implementation _isElevated = ::Microsoft::Console::Utils::IsRunningElevated(); _canDragDrop = ::Microsoft::Console::Utils::CanUwpDragDrop(); - _reloadSettings = std::make_shared>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() { - if (auto self{ weakSelf.get() }) - { - self->ReloadSettings(); - } - }); + _reloadSettings = std::make_shared>( + DispatcherQueue::GetForCurrentThread(), + til::throttled_func_options{ + .delay = std::chrono::milliseconds{ 100 }, + .debounce = true, + .trailing = true, + }, + [weakSelf = get_weak()]() { + if (auto self{ weakSelf.get() }) + { + self->ReloadSettings(); + } + }); _languageProfileNotifier = winrt::make_self([this]() { _reloadSettings->Run(); @@ -194,7 +201,7 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Attempt to load the settings. If we fail for any reason, returns an error. // Return Value: - // - S_OK if we successfully parsed the settings, otherwise an appropriate HRESULT. + // - S_OK if we successfully parsed the settings; otherwise, an appropriate HRESULT. [[nodiscard]] HRESULT AppLogic::_TryLoadSettings() noexcept { auto hr = E_FAIL; @@ -392,7 +399,7 @@ namespace winrt::TerminalApp::implementation auto ev = winrt::make_self(true, static_cast(_settingsLoadedResult), _settingsLoadExceptionText, - warnings, + warnings.GetView(), _settings); SettingsChanged.raise(*this, *ev); return; @@ -424,7 +431,7 @@ namespace winrt::TerminalApp::implementation auto ev = winrt::make_self(!initialLoad, _settingsLoadedResult, _settingsLoadExceptionText, - warnings, + warnings.GetView(), _settings); SettingsChanged.raise(*this, *ev); } @@ -491,7 +498,7 @@ namespace winrt::TerminalApp::implementation auto ev = winrt::make_self(false, _settingsLoadedResult, _settingsLoadExceptionText, - warnings, + warnings.GetView(), _settings); auto window = winrt::make_self(*ev, _contentManager); diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 0b76939992..f992561632 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -64,7 +64,7 @@ namespace winrt::TerminalApp::implementation bool _hasSettingsStartupActions{ false }; ::TerminalApp::AppCommandlineArgs _settingsAppArgs; - std::shared_ptr> _reloadSettings; + std::shared_ptr> _reloadSettings; std::vector _warnings{}; diff --git a/src/cascadia/TerminalApp/BasePaletteItem.h b/src/cascadia/TerminalApp/BasePaletteItem.h new file mode 100644 index 0000000000..c4ee9ce301 --- /dev/null +++ b/src/cascadia/TerminalApp/BasePaletteItem.h @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +namespace winrt::TerminalApp::implementation +{ + template + struct BasePaletteItem + { + public: + winrt::TerminalApp::PaletteItemType Type() { return Ty; } + + Windows::UI::Xaml::Controls::IconElement ResolvedIcon() + { + const auto icon{ static_cast(this)->Icon() }; + if (!icon.empty()) + { + const auto resolvedIcon{ Microsoft::Terminal::UI::IconPathConverter::IconWUX(icon) }; + resolvedIcon.Width(16); + resolvedIcon.Height(16); + return resolvedIcon; + } + return nullptr; + } + + til::property_changed_event PropertyChanged; + + protected: + void BaseRaisePropertyChanged(wil::zwstring_view property) + { + PropertyChanged.raise(*static_cast(this), winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs{ property }); + } + + void InvalidateResolvedIcon() + { + BaseRaisePropertyChanged(L"ResolvedIcon"); + } + }; +} diff --git a/src/cascadia/TerminalApp/ColorHelper.cpp b/src/cascadia/TerminalApp/ColorHelper.cpp deleted file mode 100644 index c2dd4c2303..0000000000 --- a/src/cascadia/TerminalApp/ColorHelper.cpp +++ /dev/null @@ -1,267 +0,0 @@ -#include "ColorHelper.h" - -using namespace winrt::TerminalApp; - -// Method Description: -// Determines whether or not a given color is light -// Arguments: -// - color: this color is going to be examined whether it -// is light or not -// Return Value: -// - true of light, false if dark -bool ColorHelper::IsBrightColor(const winrt::Windows::UI::Color& color) -{ - // https://www.w3.org/TR/AERT#color-contrast - auto brightness = (color.R * 299 + color.G * 587 + color.B * 114) / 1000.f; - return brightness > 128.f; -} - -// Method Description: -// Converts a rgb color to an hsl one -// Arguments: -// - color: the rgb color, which is going to be converted -// Return Value: -// - a hsl color with the following ranges -// - H: [0.f -360.f] -// - L: [0.f - 1.f] (rounded to the third decimal place) -// - S: [0.f - 1.f] (rounded to the third decimal place) -HSL ColorHelper::RgbToHsl(const winrt::Windows::UI::Color& color) -{ - // https://www.rapidtables.com/convert/color/rgb-to-hsl.html - auto epsilon = std::numeric_limits::epsilon(); - auto r = color.R / 255.f; - auto g = color.G / 255.f; - auto b = color.B / 255.f; - - auto max = std::max(r, std::max(g, b)); - auto min = std::min(r, std::min(g, b)); - - auto delta = max - min; - - auto h = 0.f; - auto s = 0.f; - auto l = (max + min) / 2; - - if (delta < epsilon || max < epsilon) /* delta == 0 || max == 0*/ - { - l = std::roundf(l * 1000) / 1000; - return HSL{ h, s, l }; - } - - s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min); - - if (max - r < epsilon) // max == r - { - h = (g - b) / delta + (g < b ? 6 : 0); - } - else if (max - g < epsilon) // max == g - { - h = (b - r) / delta + 2; - } - else if (max - b < epsilon) // max == b - { - h = (r - g) / delta + 4; - } - - // three decimal places after the comma ought - // to be enough for everybody - Bill Gates, 1981 - auto finalH = std::roundf(h * 60); - auto finalS = std::roundf(s * 1000) / 1000; - auto finalL = std::roundf(l * 1000) / 1000; - - return HSL{ finalH, finalS, finalL }; -} - -// Method Description: -// Converts a hsl color to rgb one -// Arguments: -// - color: the hsl color, which is going to be converted -// Return Value: -// - the rgb color (r,g,b - [0, 255] range) -winrt::Windows::UI::Color ColorHelper::HslToRgb(const HSL& color) -{ - auto epsilon = std::numeric_limits::epsilon(); - - auto h = (color.H - 1.f > epsilon) ? color.H / 360.f : color.H; - auto s = (color.S - 1.f > epsilon) ? color.S / 100.f : color.S; - auto l = (color.L - 1.f > epsilon) ? color.L / 100.f : color.L; - - auto r = l; - auto g = l; - auto b = l; - - if (s > epsilon) - { - auto q = l < 0.5 ? l * (1 + s) : l + s - l * s; - auto p = 2 * l - q; - r = HueToRgb(p, q, h + 1.f / 3.f); - g = HueToRgb(p, q, h); - b = HueToRgb(p, q, h - 1.f / 3.f); - } - - auto finalR = static_cast(std::roundf(r * 255)); - auto finalG = static_cast(std::roundf(g * 255)); - auto finalB = static_cast(std::roundf(b * 255)); - uint8_t finalA = 255; //opaque - - return winrt::Windows::UI::ColorHelper::FromArgb(finalA, finalR, finalG, finalB); -} - -float ColorHelper::HueToRgb(float p, float q, float t) -{ - auto epsilon = std::numeric_limits::epsilon(); - - if (t < 0) - t += 1; - if (t > 1) - t -= 1; - if (t - (1.f / 6.f) < epsilon) - return p + (q - p) * 6 * t; - if (t - .5f < epsilon) - return q; - if (t - 2.f / 3.f < epsilon) - return p + (q - p) * (2.f / 3.f - t) * 6; - return p; -} - -// Method Description: -// Lightens a color by a given amount -// Arguments: -// - color: the color which is going to be lightened -// - amount: the lighten amount (0-100) -// Return Value: -// - the lightened color in RGB format -winrt::Windows::UI::Color ColorHelper::Lighten(const winrt::Windows::UI::Color& color, float amount /* = 10.f*/) -{ - auto hsl = RgbToHsl(color); - hsl.L += amount / 100; - hsl.L = std::clamp(hsl.L, 0.f, 1.f); - return HslToRgb(hsl); -} - -// Method Description: -// Darkens a color by a given amount -// Arguments: -// - color: the color which is going to be darkened -// - amount: the darken amount (0-100) -// Return Value: -// - the darkened color in RGB format -winrt::Windows::UI::Color ColorHelper::Darken(const winrt::Windows::UI::Color& color, float amount /* = 10.f*/) -{ - auto hsl = RgbToHsl(color); - hsl.L -= amount / 100; - hsl.L = std::clamp(hsl.L, 0.f, 1.f); - return HslToRgb(hsl); -} - -// Method Description: -// Gets an accent color to a given color. Basically, generates -// 16 shades of the color and finds the first which has a good -// contrast according to https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2) -// Readability ratio of 3.5 seems to look quite nicely -// Arguments: -// - color: the color for which we need an accent -// Return Value: -// - the accent color in RGB format -winrt::Windows::UI::Color ColorHelper::GetAccentColor(const winrt::Windows::UI::Color& color) -{ - auto accentColor = RgbToHsl(color); - - if (accentColor.S < 0.15) - { - accentColor.S = 0.15f; - } - - constexpr auto shadeCount = 16; - constexpr auto shadeStep = 1.f / shadeCount; - auto shades = std::map(); - for (auto i = 0; i < 15; i++) - { - auto shade = HSL{ accentColor.H, accentColor.S, i * shadeStep }; - auto contrast = GetReadability(shade, accentColor); - shades.insert(std::make_pair(contrast, shade)); - } - - // 3f is quite nice if the whole non-client area is painted - constexpr auto readability = 1.75f; - for (auto shade : shades) - { - if (shade.first >= readability) - { - return HslToRgb(shade.second); - } - } - return HslToRgb(shades.end()->second); -} - -// Method Description: -// Gets the readability of two colors according to -// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2) -// Arguments: -// - firstColor: the first color for the readability check (hsl) -// - secondColor: the second color for the readability check (hsl) -// Return Value: -// - the readability of the colors according to (WCAG Version 2) -float ColorHelper::GetReadability(const HSL& first, const HSL& second) -{ - return GetReadability(HslToRgb(first), HslToRgb(second)); -} - -// Method Description: -// Gets the readability of two colors according to -// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2) -// Arguments: -// - firstColor: the first color for the readability check (rgb) -// - secondColor: the second color for the readability check (rgb) -// Return Value: -// - the readability of the colors according to (WCAG Version 2) -float ColorHelper::GetReadability(const winrt::Windows::UI::Color& first, const winrt::Windows::UI::Color& second) -{ - auto l1 = GetLuminance(first); - auto l2 = GetLuminance(second); - - return (std::max(l1, l2) + 0.05f) / std::min(l1, l2) + 0.05f; -} - -// Method Description: -// Calculates the luminance of a given color according to -// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef -// Arguments: -// - color: its luminance is going to be calculated -// Return Value: -// - the luminance of the color -float ColorHelper::GetLuminance(const winrt::Windows::UI::Color& color) -{ - auto epsilon = std::numeric_limits::epsilon(); - float R, G, B; - auto RsRGB = color.R / 255.f; - auto GsRGB = color.G / 255.f; - auto BsRGB = color.B / 255.f; - - if (RsRGB - 0.03928f <= epsilon) - { - R = RsRGB / 12.92f; - } - else - { - R = std::pow(((RsRGB + 0.055f) / 1.055f), 2.4f); - } - if (GsRGB - 0.03928f <= epsilon) - { - G = GsRGB / 12.92f; - } - else - { - G = std::pow(((GsRGB + 0.055f) / 1.055f), 2.4f); - } - if (BsRGB - 0.03928f <= epsilon) - { - B = BsRGB / 12.92f; - } - else - { - B = std::pow(((BsRGB + 0.055f) / 1.055f), 2.4f); - } - auto luminance = (0.2126f * R) + (0.7152f * G) + (0.0722f * B); - return std::roundf(luminance * 10000) / 10000.f; -} diff --git a/src/cascadia/TerminalApp/ColorHelper.h b/src/cascadia/TerminalApp/ColorHelper.h deleted file mode 100644 index e4ab3f42b7..0000000000 --- a/src/cascadia/TerminalApp/ColorHelper.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -namespace winrt::TerminalApp -{ - class HSL - { - public: - float H; - float S; - float L; - }; - - class ColorHelper - { - public: - static bool IsBrightColor(const Windows::UI::Color& color); - static HSL RgbToHsl(const Windows::UI::Color& color); - static Windows::UI::Color HslToRgb(const HSL& color); - static Windows::UI::Color Lighten(const Windows::UI::Color& color, float amount = 10.f); - static Windows::UI::Color Darken(const Windows::UI::Color& color, float amount = 10.f); - static Windows::UI::Color GetAccentColor(const Windows::UI::Color& color); - static float GetLuminance(const Windows::UI::Color& color); - static float GetReadability(const Windows::UI::Color& first, const Windows::UI::Color& second); - static float GetReadability(const HSL& first, const HSL& second); - - private: - static float HueToRgb(float p, float q, float t); - }; -} diff --git a/src/cascadia/TerminalApp/CommandLinePaletteItem.cpp b/src/cascadia/TerminalApp/CommandLinePaletteItem.cpp deleted file mode 100644 index 5cb7170d39..0000000000 --- a/src/cascadia/TerminalApp/CommandLinePaletteItem.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "CommandLinePaletteItem.h" -#include - -#include "CommandLinePaletteItem.g.cpp" - -using namespace winrt; -using namespace winrt::TerminalApp; -using namespace winrt::Windows::UI::Core; -using namespace winrt::Windows::UI::Xaml; -using namespace winrt::Windows::System; -using namespace winrt::Windows::Foundation; -using namespace winrt::Windows::Foundation::Collections; -using namespace winrt::Microsoft::Terminal::Settings::Model; - -namespace winrt::TerminalApp::implementation -{ - CommandLinePaletteItem::CommandLinePaletteItem(const winrt::hstring& commandLine) : - _CommandLine(commandLine) - { - Name(commandLine); - } -} diff --git a/src/cascadia/TerminalApp/CommandLinePaletteItem.h b/src/cascadia/TerminalApp/CommandLinePaletteItem.h deleted file mode 100644 index 725449478b..0000000000 --- a/src/cascadia/TerminalApp/CommandLinePaletteItem.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once - -#include "PaletteItem.h" -#include "CommandLinePaletteItem.g.h" - -namespace winrt::TerminalApp::implementation -{ - struct CommandLinePaletteItem : CommandLinePaletteItemT - { - CommandLinePaletteItem() = default; - CommandLinePaletteItem(const winrt::hstring& commandLine); - - WINRT_PROPERTY(winrt::hstring, CommandLine); - }; -} - -namespace winrt::TerminalApp::factory_implementation -{ - BASIC_FACTORY(CommandLinePaletteItem); -} diff --git a/src/cascadia/TerminalApp/CommandLinePaletteItem.idl b/src/cascadia/TerminalApp/CommandLinePaletteItem.idl deleted file mode 100644 index 5b1244aae9..0000000000 --- a/src/cascadia/TerminalApp/CommandLinePaletteItem.idl +++ /dev/null @@ -1,12 +0,0 @@ -import "PaletteItem.idl"; -import "TabBase.idl"; - -namespace TerminalApp -{ - [default_interface] runtimeclass CommandLinePaletteItem : PaletteItem - { - CommandLinePaletteItem(String commandLine); - - String CommandLine { get; }; - } -} diff --git a/src/cascadia/TerminalApp/CommandPalette.cpp b/src/cascadia/TerminalApp/CommandPalette.cpp index 6d5540703f..6caca24036 100644 --- a/src/cascadia/TerminalApp/CommandPalette.cpp +++ b/src/cascadia/TerminalApp/CommandPalette.cpp @@ -2,10 +2,8 @@ // Licensed under the MIT license. #include "pch.h" -#include "ActionPaletteItem.h" -#include "TabPaletteItem.h" -#include "CommandLinePaletteItem.h" #include "CommandPalette.h" +#include "CommandPaletteItems.h" #include #include "CommandPalette.g.cpp" @@ -233,9 +231,11 @@ namespace winrt::TerminalApp::implementation } else if (_currentMode == CommandPaletteMode::ActionMode && filteredCommand != nullptr) { - if (const auto actionPaletteItem{ filteredCommand.Item().try_as() }) + const auto item{ filteredCommand.Item() }; + if (item.Type() == PaletteItemType::Action) { - PreviewAction.raise(*this, actionPaletteItem.Command()); + const auto actionPaletteItem{ winrt::get_self(item) }; + PreviewAction.raise(*this, actionPaletteItem->Command()); } } else if (_currentMode == CommandPaletteMode::CommandlineMode) @@ -338,7 +338,7 @@ namespace winrt::TerminalApp::implementation } else if (key == VirtualKey::Escape) { - // Dismiss the palette if the text is empty, otherwise clear the + // Dismiss the palette if the text is empty; otherwise, clear the // search string. if (_searchBox().Text().empty()) { @@ -555,10 +555,11 @@ namespace winrt::TerminalApp::implementation const auto enteredItem = listViewItem.Content(); if (const auto filteredCommand{ enteredItem.try_as() }) { - if (const auto actionPaletteItem{ filteredCommand.Item().try_as() }) + const auto item{ filteredCommand.Item() }; + if (item.Type() == PaletteItemType::Action) { - // immediately preview the hovered command - PreviewAction.raise(*this, actionPaletteItem.Command()); + const auto actionPaletteItem{ winrt::get_self(item) }; + PreviewAction.raise(*this, actionPaletteItem->Command()); } } } @@ -589,9 +590,11 @@ namespace winrt::TerminalApp::implementation { if (_currentMode == CommandPaletteMode::ActionMode && filteredCommand) { - if (const auto actionPaletteItem{ filteredCommand.Item().try_as() }) + const auto item{ filteredCommand.Item() }; + if (item.Type() == PaletteItemType::Action) { - PreviewAction.raise(*this, actionPaletteItem.Command()); + const auto actionPaletteItem{ winrt::get_self(item) }; + PreviewAction.raise(*this, actionPaletteItem->Command()); } } } @@ -617,7 +620,7 @@ namespace winrt::TerminalApp::implementation const auto selectedCommand = selectedList.GetAt(0); if (const auto filteredCmd = selectedCommand.try_as()) { - if (const auto paletteItem = filteredCmd.Item().try_as()) + if (const auto paletteItem = filteredCmd.Item()) { automationPeer.RaiseNotificationEvent( Automation::Peers::AutomationNotificationKind::ItemAdded, @@ -652,10 +655,13 @@ namespace winrt::TerminalApp::implementation if (_nestedActionStack.Size() > 0) { const auto newPreviousAction{ _nestedActionStack.GetAt(_nestedActionStack.Size() - 1) }; - const auto actionPaletteItem{ newPreviousAction.Item().try_as() }; - - ParentCommandName(actionPaletteItem.Command().Name()); - _updateCurrentNestedCommands(actionPaletteItem.Command()); + const auto item{ newPreviousAction.Item() }; + if (item.Type() == PaletteItemType::Action) + { + const auto actionPaletteItem{ winrt::get_self(item) }; + ParentCommandName(actionPaletteItem->Command().Name()); + _updateCurrentNestedCommands(actionPaletteItem->Command()); + } } else { @@ -757,16 +763,19 @@ namespace winrt::TerminalApp::implementation } else if (filteredCommand) { - if (const auto actionPaletteItem{ filteredCommand.Item().try_as() }) + auto item{ filteredCommand.Item() }; + if (item.Type() == PaletteItemType::Action) { - if (actionPaletteItem.Command().HasNestedCommands()) + const auto actionPaletteItem{ winrt::get_self(item) }; + auto command{ actionPaletteItem->Command() }; + if (command.HasNestedCommands()) { // If this Command had subcommands, then don't dispatch the // action. Instead, display a new list of commands for the user // to pick from. _nestedActionStack.Append(filteredCommand); - ParentCommandName(actionPaletteItem.Command().Name()); - _updateCurrentNestedCommands(actionPaletteItem.Command()); + ParentCommandName(command.Name()); + _updateCurrentNestedCommands(command); _updateUIForStackChange(); } @@ -785,9 +794,9 @@ namespace winrt::TerminalApp::implementation // But make an exception for the Toggle Command Palette action: we don't want the dispatch // make the command palette - that was just closed - visible again. // All other actions can just be dispatched. - if (actionPaletteItem.Command().ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette) + if (command.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette) { - DispatchCommandRequested.raise(*this, actionPaletteItem.Command()); + DispatchCommandRequested.raise(*this, command); } TraceLoggingWrite( @@ -837,9 +846,11 @@ namespace winrt::TerminalApp::implementation { if (filteredCommand) { - if (const auto tabPaletteItem{ filteredCommand.Item().try_as() }) + const auto item{ filteredCommand.Item() }; + if (item.Type() == PaletteItemType::Tab) { - if (const auto tab{ tabPaletteItem.Tab() }) + const auto tabPaletteItem{ winrt::get_self(item) }; + if (const auto tab{ tabPaletteItem->Tab() }) { SwitchToTabRequested.raise(*this, tab); } @@ -867,9 +878,11 @@ namespace winrt::TerminalApp::implementation TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - if (const auto commandLinePaletteItem{ filteredCommand.value().Item().try_as() }) + const auto item{ filteredCommand->Item() }; + if (item.Type() == PaletteItemType::CommandLine) { - CommandLineExecutionRequested.raise(*this, commandLinePaletteItem.CommandLine()); + const auto commandLinePaletteItem{ winrt::get_self(item) }; + CommandLineExecutionRequested.raise(*this, commandLinePaletteItem->CommandLine()); _close(); } } @@ -1059,7 +1072,7 @@ namespace winrt::TerminalApp::implementation // Return Value: // - void CommandPalette::_bindTabs( - const Windows::Foundation::Collections::IObservableVector& source, + const Windows::Foundation::Collections::IObservableVector& source, const Windows::Foundation::Collections::IVector& target) { target.Clear(); @@ -1071,7 +1084,7 @@ namespace winrt::TerminalApp::implementation } } - void CommandPalette::SetTabs(const Collections::IObservableVector& tabs, const Collections::IObservableVector& mruTabs) + void CommandPalette::SetTabs(const Collections::IObservableVector& tabs, const Collections::IObservableVector& mruTabs) { _bindTabs(tabs, _tabActions); _bindTabs(mruTabs, _mruTabActions); @@ -1174,12 +1187,15 @@ namespace winrt::TerminalApp::implementation } else if (_currentMode == CommandPaletteMode::TabSearchMode || _currentMode == CommandPaletteMode::ActionMode || _currentMode == CommandPaletteMode::CommandlineMode) { + auto pattern = std::make_shared(fzf::matcher::ParsePattern(searchText)); + for (const auto& action : commandsToFilter) { // Update filter for all commands // This will modify the highlighting but will also lead to re-computation of weight (and consequently sorting). // Pay attention that it already updates the highlighting in the UI - action.UpdateFilter(searchText); + auto impl = winrt::get_self(action); + impl->UpdateFilter(pattern); // if there is active search we skip commands with 0 weight if (searchText.empty() || action.Weight() > 0) diff --git a/src/cascadia/TerminalApp/CommandPalette.h b/src/cascadia/TerminalApp/CommandPalette.h index 0da7ee1334..8e621b104d 100644 --- a/src/cascadia/TerminalApp/CommandPalette.h +++ b/src/cascadia/TerminalApp/CommandPalette.h @@ -31,7 +31,7 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IObservableVector FilteredActions(); - void SetTabs(const Windows::Foundation::Collections::IObservableVector& tabs, const Windows::Foundation::Collections::IObservableVector& mruTabs); + void SetTabs(const Windows::Foundation::Collections::IObservableVector& tabs, const Windows::Foundation::Collections::IObservableVector& mruTabs); void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap); bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); @@ -48,7 +48,7 @@ namespace winrt::TerminalApp::implementation void EnableTabSearchMode(); til::property_changed_event PropertyChanged; - til::typed_event SwitchToTabRequested; + til::typed_event SwitchToTabRequested; til::typed_event CommandLineExecutionRequested; til::typed_event DispatchCommandRequested; til::typed_event PreviewAction; @@ -135,7 +135,7 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::TabSwitcherMode _tabSwitcherMode; uint32_t _switcherStartIdx; - void _bindTabs(const Windows::Foundation::Collections::IObservableVector& source, const Windows::Foundation::Collections::IVector& target); + void _bindTabs(const Windows::Foundation::Collections::IObservableVector& source, const Windows::Foundation::Collections::IVector& target); void _anchorKeyUpHandler(); winrt::Windows::UI::Xaml::Controls::ListView::SizeChanged_revoker _sizeChangedRevoker; diff --git a/src/cascadia/TerminalApp/CommandPalette.idl b/src/cascadia/TerminalApp/CommandPalette.idl index 48787135a9..79fa26d736 100644 --- a/src/cascadia/TerminalApp/CommandPalette.idl +++ b/src/cascadia/TerminalApp/CommandPalette.idl @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import "TabBase.idl"; +import "Tab.idl"; import "HighlightedTextControl.idl"; import "FilteredCommand.idl"; @@ -20,7 +20,7 @@ namespace TerminalApp Windows.Foundation.Collections.IObservableVector FilteredActions { get; }; - void SetTabs(Windows.Foundation.Collections.IObservableVector tabs, Windows.Foundation.Collections.IObservableVector mruTabs); + void SetTabs(Windows.Foundation.Collections.IObservableVector tabs, Windows.Foundation.Collections.IObservableVector mruTabs); void SetActionMap(Microsoft.Terminal.Settings.Model.IActionMapView actionMap); @@ -30,7 +30,7 @@ namespace TerminalApp void EnableTabSwitcherMode(UInt32 startIdx, Microsoft.Terminal.Settings.Model.TabSwitcherMode tabSwitcherMode); void EnableTabSearchMode(); - event Windows.Foundation.TypedEventHandler SwitchToTabRequested; + event Windows.Foundation.TypedEventHandler SwitchToTabRequested; event Windows.Foundation.TypedEventHandler DispatchCommandRequested; event Windows.Foundation.TypedEventHandler CommandLineExecutionRequested; event Windows.Foundation.TypedEventHandler PreviewAction; diff --git a/src/cascadia/TerminalApp/CommandPalette.xaml b/src/cascadia/TerminalApp/CommandPalette.xaml index 7a4b598d5b..b933173c77 100644 --- a/src/cascadia/TerminalApp/CommandPalette.xaml +++ b/src/cascadia/TerminalApp/CommandPalette.xaml @@ -44,6 +44,12 @@ + + - + + + + + + + + + diff --git a/src/cascadia/TerminalApp/IPaletteItem.idl b/src/cascadia/TerminalApp/IPaletteItem.idl new file mode 100644 index 0000000000..7cb73db0b8 --- /dev/null +++ b/src/cascadia/TerminalApp/IPaletteItem.idl @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "TerminalTabStatus.idl"; + +namespace TerminalApp +{ + enum PaletteItemType + { + Action, + CommandLine, + Tab, + }; + + interface IPaletteItem requires Windows.UI.Xaml.Data.INotifyPropertyChanged + { + PaletteItemType Type { get; }; + String Name { get; }; + String Subtitle { get; }; + String KeyChordText { get; }; + String Icon { get; }; + Windows.UI.Xaml.Controls.IconElement ResolvedIcon { get; }; + } + + runtimeclass TabPaletteItem : [default] IPaletteItem, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + TerminalTabStatus TabStatus{ get; }; + } +} diff --git a/src/cascadia/TerminalApp/Jumplist.cpp b/src/cascadia/TerminalApp/Jumplist.cpp index d558c5433f..e713514973 100644 --- a/src/cascadia/TerminalApp/Jumplist.cpp +++ b/src/cascadia/TerminalApp/Jumplist.cpp @@ -19,41 +19,6 @@ DEFINE_PROPERTYKEY(PKEY_AppUserModel_DestListLogoUri, 0x9F4C2855, 0x9F79, 0x4B39 { 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 }, 29 \ } -// Function Description: -// - This function guesses whether a string is a file path. -static constexpr bool _isProbableFilePath(std::wstring_view path) -{ - // "C:X", "C:\X", "\\?", "\\." - // _this function rejects \??\ as a path_ - if (path.size() >= 3) - { - const auto firstColon{ path.find(L':') }; - if (firstColon == 1) - { - return true; - } - - const auto prefix{ path.substr(0, 2) }; - return prefix == LR"(//)" || prefix == LR"(\\)"; - } - return false; -} - -// Function Description: -// - DestListLogoUri cannot take paths that are separated by / unless they're URLs. -// This function uses std::filesystem to normalize strings that appear to be file -// paths to have the "correct" slash direction. -static std::wstring _normalizeIconPath(std::wstring_view path) -{ - const auto fullPath{ wil::ExpandEnvironmentStringsW(path.data()) }; - if (_isProbableFilePath(fullPath)) - { - std::filesystem::path asPath{ fullPath }; - return asPath.make_preferred().wstring(); - } - return std::wstring{ fullPath }; -} - // Method Description: // - Updates the items of the Jumplist based on the given settings. // Arguments: @@ -124,7 +89,7 @@ void Jumplist::_updateProfiles(IObjectCollection* jumplistItems, winrt::Windows: auto args = fmt::format(FMT_COMPILE(L"-p {}"), to_hstring(profile.Guid())); // Create the shell link object for the profile - const auto normalizedIconPath{ _normalizeIconPath(profile.Icon()) }; + const auto normalizedIconPath{ profile.Icon().Resolved() }; const auto shLink = _createShellLink(profile.Name(), normalizedIconPath, args); THROW_IF_FAILED(jumplistItems->AddObject(shLink.get())); } diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp index f4db97176e..541f7351ae 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp @@ -35,17 +35,23 @@ namespace winrt::TerminalApp::implementation // (which should be the default, see: // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-trackmouseevent#remarks) unsigned int hoverTimeoutMillis{ 400 }; - LOG_IF_WIN32_BOOL_FALSE(SystemParametersInfoW(SPI_GETMOUSEHOVERTIME, 0, &hoverTimeoutMillis, 0)); - const auto toolTipInterval = std::chrono::milliseconds(hoverTimeoutMillis); + if (FAILED(SystemParametersInfoW(SPI_GETMOUSEHOVERTIME, 0, &hoverTimeoutMillis, 0))) + { + hoverTimeoutMillis = 400; + } // Create a ThrottledFunc for opening the tooltip after the hover // timeout. If we hover another button, we should make sure to call // Run() with the new button. Calling `_displayToolTip.Run(nullptr)`, // which will cause us to not display a tooltip, which is used when we // leave the control entirely. - _displayToolTip = std::make_shared>( + _displayToolTip = std::make_shared>( dispatcher, - toolTipInterval, + til::throttled_func_options{ + .delay = std::chrono::milliseconds{ hoverTimeoutMillis }, + .debounce = true, + .trailing = true, + }, [weakThis = get_weak()](Controls::Button button) { // If we provide a button, then open the tooltip on that button. // We can "dismiss" this throttled func by calling it with null, diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.h b/src/cascadia/TerminalApp/MinMaxCloseControl.h index 5bda6974f4..4a53674174 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.h +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.h @@ -32,7 +32,7 @@ namespace winrt::TerminalApp::implementation til::typed_event MaximizeClick; til::typed_event CloseClick; - std::shared_ptr> _displayToolTip{ nullptr }; + std::shared_ptr> _displayToolTip{ nullptr }; std::optional _lastPressedButton{ std::nullopt }; }; } diff --git a/src/cascadia/TerminalApp/PaletteItem.cpp b/src/cascadia/TerminalApp/PaletteItem.cpp deleted file mode 100644 index c72916fcb3..0000000000 --- a/src/cascadia/TerminalApp/PaletteItem.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include -#include "PaletteItem.h" -#include "PaletteItem.g.cpp" - -using namespace winrt; -using namespace winrt::Windows::UI::Xaml; -using namespace winrt::Windows::UI::Core; -using namespace winrt::Microsoft::Terminal::Control; -using namespace winrt::Microsoft::Terminal::Settings::Model; -using namespace winrt::Windows::System; - -namespace winrt::TerminalApp::implementation -{ - Controls::IconElement PaletteItem::ResolvedIcon() - { - const auto icon = Microsoft::Terminal::UI::IconPathConverter::IconWUX(Icon()); - icon.Width(16); - icon.Height(16); - return icon; - } -} diff --git a/src/cascadia/TerminalApp/PaletteItem.h b/src/cascadia/TerminalApp/PaletteItem.h deleted file mode 100644 index f1cea01905..0000000000 --- a/src/cascadia/TerminalApp/PaletteItem.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once -#include "PaletteItem.g.h" - -namespace winrt::TerminalApp::implementation -{ - struct PaletteItem : PaletteItemT - { - public: - Windows::UI::Xaml::Controls::IconElement ResolvedIcon(); - - til::property_changed_event PropertyChanged; - - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, PropertyChanged.raise); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Icon, PropertyChanged.raise); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, KeyChordText, PropertyChanged.raise); - }; -} diff --git a/src/cascadia/TerminalApp/PaletteItem.idl b/src/cascadia/TerminalApp/PaletteItem.idl deleted file mode 100644 index c4273de5d1..0000000000 --- a/src/cascadia/TerminalApp/PaletteItem.idl +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -namespace TerminalApp -{ - unsealed runtimeclass PaletteItem : Windows.UI.Xaml.Data.INotifyPropertyChanged - { - String Name; - String KeyChordText; - String Icon; - Windows.UI.Xaml.Controls.IconElement ResolvedIcon { get; }; - } -} diff --git a/src/cascadia/TerminalApp/PaletteItemTemplateSelector.cpp b/src/cascadia/TerminalApp/PaletteItemTemplateSelector.cpp index ca67d6f07a..1d5afeb111 100644 --- a/src/cascadia/TerminalApp/PaletteItemTemplateSelector.cpp +++ b/src/cascadia/TerminalApp/PaletteItemTemplateSelector.cpp @@ -2,10 +2,11 @@ // Licensed under the MIT license. #include "pch.h" -#include "TabPaletteItem.h" #include "PaletteItemTemplateSelector.h" #include "PaletteItemTemplateSelector.g.cpp" +#include "CommandPaletteItems.h" + namespace winrt::TerminalApp::implementation { Windows::UI::Xaml::DataTemplate PaletteItemTemplateSelector::SelectTemplateCore(const winrt::Windows::Foundation::IInspectable& item, const winrt::Windows::UI::Xaml::DependencyObject& /*container*/) @@ -26,16 +27,22 @@ namespace winrt::TerminalApp::implementation { if (const auto filteredCommand{ item.try_as() }) { - if (filteredCommand.Item().try_as()) + switch (filteredCommand.Item().Type()) { + case PaletteItemType::Tab: return TabItemTemplate(); - } - else if (const auto actionPaletteItem{ filteredCommand.Item().try_as() }) + case PaletteItemType::Action: { - if (actionPaletteItem.Command().HasNestedCommands()) + const auto actionPaletteItem{ winrt::get_self(filteredCommand.Item()) }; + if (actionPaletteItem->Command().HasNestedCommands()) { return NestedItemTemplate(); } + break; // Fall back to the general template + } + case PaletteItemType::CommandLine: + default: + break; // Fall back to the general template } } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index dc235b0602..1afe0d118a 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -697,12 +697,24 @@ bool Pane::SwapPanes(std::shared_ptr first, std::shared_ptr second) // Refocus the last pane if there was a pane focused if (const auto focus = first->GetActivePane()) { - focus->_Focus(); + // GH#18184: manually focus the pane and content. + // _Focus() results in no-op because the pane was _lastActive + focus->GotFocus.raise(focus, FocusState::Programmatic); + if (const auto& lastContent{ focus->GetLastFocusedContent() }) + { + lastContent.Focus(FocusState::Programmatic); + } } if (const auto focus = second->GetActivePane()) { - focus->_Focus(); + // GH#18184: manually focus the pane and content. + // _Focus() results in no-op because the pane was _lastActive + focus->GotFocus.raise(focus, FocusState::Programmatic); + if (const auto& lastContent{ focus->GetLastFocusedContent() }) + { + lastContent.Focus(FocusState::Programmatic); + } } return true; @@ -1040,7 +1052,7 @@ std::shared_ptr Pane::GetActivePane() // Arguments: // - // Return Value: -// - nullptr if this Pane is an unfocused parent, otherwise the TermControl of this Pane. +// - nullptr if this Pane is an unfocused parent; otherwise, the TermControl of this Pane. TermControl Pane::GetLastFocusedTerminalControl() { if (!_IsLeaf()) @@ -1093,7 +1105,7 @@ IPaneContent Pane::GetLastFocusedContent() // Arguments: // - // Return Value: -// - nullptr if this Pane is a parent, otherwise the TermControl of this Pane. +// - nullptr if this Pane is a parent; otherwise, the TermControl of this Pane. TermControl Pane::GetTerminalControl() const { if (const auto& terminalPane{ _getTerminalContent() }) @@ -1402,6 +1414,13 @@ void Pane::_CloseChild(const bool closeFirst) // take the control, profile, id and isDefTermSession of the pane that _wasn't_ closed. _setPaneContent(remainingChild->_takePaneContent()); + if (!_content) + { + // GH#18071: our content is still null after taking the other pane's content, + // so just notify our parent that we're closed. + Closed.raise(nullptr, nullptr); + return; + } _id = remainingChild->Id(); // Revoke the old event handlers. Remove both the handlers for the panes @@ -1585,7 +1604,7 @@ void Pane::_CloseChildRoutine(const bool closeFirst) return; } - // Setup the animation + // Set up the animation auto removedChild = closeFirst ? _firstChild : _secondChild; auto remainingChild = closeFirst ? _secondChild : _firstChild; @@ -1986,7 +2005,7 @@ void Pane::_SetupEntranceAnimation() if (splitWidth) { // If we're animating the first child, then stick to the top/left of - // the parent pane, otherwise use the bottom/right. This is always + // the parent pane; otherwise, use the bottom/right. This is always // the "outside" of the parent pane. childGrid.HorizontalAlignment(isFirstChild ? HorizontalAlignment::Left : HorizontalAlignment::Right); if (control) @@ -2011,7 +2030,7 @@ void Pane::_SetupEntranceAnimation() else { // If we're animating the first child, then stick to the top/left of - // the parent pane, otherwise use the bottom/right. This is always + // the parent pane; otherwise, use the bottom/right. This is always // the "outside" of the parent pane. childGrid.VerticalAlignment(isFirstChild ? VerticalAlignment::Top : VerticalAlignment::Bottom); if (control) @@ -2410,7 +2429,7 @@ std::optional Pane::Id() noexcept // Method Description: // - Sets this pane's ID -// - Panes are given IDs upon creation by TerminalTab +// - Panes are given IDs upon creation by Tab // Arguments: // - The number to set this pane's ID to void Pane::Id(uint32_t id) noexcept @@ -2526,7 +2545,7 @@ std::pair Pane::_CalcChildrenSizes(const float fullSize) const // user doesn't get any pane shrank when they actually expand the window or parent pane. // That is also required by the layout algorithm. // Arguments: -// - widthOrHeight: if true, operates on width, otherwise on height. +// - widthOrHeight: if true, operates on width; otherwise, on height. // - fullSize: the amount of space in pixels that should be filled by our children and // their separator. Can be arbitrarily low. // Return Value: @@ -2588,7 +2607,7 @@ Pane::SnapChildrenSizeResult Pane::_CalcSnappedChildrenSizes(const bool widthOrH // align with their character grids as close as possible. Snaps to closes match // (either upward or downward). Also makes sure to fit in minimal sizes of the panes. // Arguments: -// - widthOrHeight: if true operates on width, otherwise on height +// - widthOrHeight: if true operates on width; otherwise, on height // - dimension: a dimension (width or height) to snap // Return Value: // - A value corresponding to the next closest snap size for this Pane, either upward or downward @@ -2603,7 +2622,7 @@ float Pane::CalcSnappedDimension(const bool widthOrHeight, const float dimension // align with their character grids as close as possible. Also makes sure to // fit in minimal sizes of the panes. // Arguments: -// - widthOrHeight: if true operates on width, otherwise on height +// - widthOrHeight: if true operates on width; otherwise, on height // - dimension: a dimension (width or height) to be snapped // Return Value: // - pair of floats, where first value is the size snapped downward (not greater than @@ -2690,11 +2709,11 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const // Method Description: // - Increases size of given LayoutSizeNode to match next possible 'snap'. In case of leaf -// pane this means the next cell of the terminal. Otherwise it means that one of its children +// pane this means the next cell of the terminal. Otherwise, it means that one of its children // advances (recursively). It expects the given node and its descendants to have either // already snapped or minimum size. // Arguments: -// - widthOrHeight: if true operates on width, otherwise on height. +// - widthOrHeight: if true operates on width; otherwise, on height. // - sizeNode: a layout size node that corresponds to this pane. // Return Value: // - @@ -2865,7 +2884,7 @@ Size Pane::_GetMinSize() const // - Builds a tree of LayoutSizeNode that matches the tree of panes. Each node // has minimum size that the corresponding pane can have. // Arguments: -// - widthOrHeight: if true operates on width, otherwise on height +// - widthOrHeight: if true operates on width; otherwise, on height // Return Value: // - Root node of built tree that matches this pane. Pane::LayoutSizeNode Pane::_CreateMinSizeTree(const bool widthOrHeight) const @@ -2885,7 +2904,7 @@ Pane::LayoutSizeNode Pane::_CreateMinSizeTree(const bool widthOrHeight) const // - Adjusts split position so that no child pane is smaller then its // minimum size // Arguments: -// - widthOrHeight: if true, operates on width, otherwise on height. +// - widthOrHeight: if true, operates on width; otherwise, on height. // - requestedValue: split position value to be clamped // - totalSize: size (width or height) of the parent pane // Return Value: diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 8bce64852d..ecc81fad82 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -31,7 +31,7 @@ namespace TerminalAppLocalTests namespace winrt::TerminalApp::implementation { - struct TerminalTab; + struct Tab; } enum class Borders : int @@ -398,6 +398,6 @@ private: LayoutSizeNode& operator=(const LayoutSizeNode& other); }; - friend struct winrt::TerminalApp::implementation::TerminalTab; + friend struct winrt::TerminalApp::implementation::Tab; friend class ::TerminalAppLocalTests::TabTests; }; diff --git a/src/cascadia/TerminalApp/Remoting.cpp b/src/cascadia/TerminalApp/Remoting.cpp index 0c8693ed46..078a0d7f11 100644 --- a/src/cascadia/TerminalApp/Remoting.cpp +++ b/src/cascadia/TerminalApp/Remoting.cpp @@ -15,19 +15,6 @@ using namespace winrt::Windows::Foundation; namespace winrt::TerminalApp::implementation { - CommandlineArgs::CommandlineArgs(winrt::array_view args, winrt::hstring currentDirectory, uint32_t showWindowCommand, winrt::hstring envString) : - _args{ args.begin(), args.end() }, - CurrentDirectory{ std::move(currentDirectory) }, - ShowWindowCommand{ showWindowCommand }, - CurrentEnvironment{ std::move(envString) } - { - _parseResult = _parsed.ParseArgs(_args); - if (_parseResult == 0) - { - _parsed.ValidateStartupCommands(); - } - } - ::TerminalApp::AppCommandlineArgs& CommandlineArgs::ParsedArgs() noexcept { return _parsed; @@ -56,6 +43,11 @@ namespace winrt::TerminalApp::implementation void CommandlineArgs::Commandline(const winrt::array_view& value) { _args = { value.begin(), value.end() }; + _parseResult = _parsed.ParseArgs(_args); + if (_parseResult == 0) + { + _parsed.ValidateStartupCommands(); + } } winrt::com_array CommandlineArgs::Commandline() diff --git a/src/cascadia/TerminalApp/Remoting.h b/src/cascadia/TerminalApp/Remoting.h index 2ad297f31e..9d4a48df76 100644 --- a/src/cascadia/TerminalApp/Remoting.h +++ b/src/cascadia/TerminalApp/Remoting.h @@ -13,21 +13,17 @@ namespace winrt::TerminalApp::implementation { struct CommandlineArgs : public CommandlineArgsT { - CommandlineArgs() = default; - CommandlineArgs(winrt::array_view args, winrt::hstring currentDirectory, uint32_t showWindowCommand, winrt::hstring envString); - ::TerminalApp::AppCommandlineArgs& ParsedArgs() noexcept; winrt::com_array& CommandlineRef() noexcept; // These bits are exposed via WinRT: - public: int32_t ExitCode() const noexcept; winrt::hstring ExitMessage() const; winrt::hstring TargetWindow() const; + til::property Connection; void Commandline(const winrt::array_view& value); winrt::com_array Commandline(); - til::property CurrentDirectory; til::property CurrentEnvironment; til::property ShowWindowCommand{ static_cast(SW_NORMAL) }; // SW_NORMAL is 1, 0 is SW_HIDE @@ -86,11 +82,9 @@ namespace winrt::TerminalApp::implementation WINRT_PROPERTY(uint64_t, Id); WINRT_PROPERTY(winrt::hstring, WindowName); - WINRT_PROPERTY(TerminalApp::CommandlineArgs, Command); + WINRT_PROPERTY(TerminalApp::CommandlineArgs, Command, nullptr); WINRT_PROPERTY(winrt::hstring, Content); WINRT_PROPERTY(Windows::Foundation::IReference, InitialBounds); - - private: }; } diff --git a/src/cascadia/TerminalApp/Remoting.idl b/src/cascadia/TerminalApp/Remoting.idl index 17f1318422..07fde64e50 100644 --- a/src/cascadia/TerminalApp/Remoting.idl +++ b/src/cascadia/TerminalApp/Remoting.idl @@ -6,16 +6,16 @@ namespace TerminalApp runtimeclass CommandlineArgs { CommandlineArgs(); - CommandlineArgs(String[] args, String cwd, UInt32 showWindowCommand, String env); Int32 ExitCode { get; }; String ExitMessage { get; }; String TargetWindow { get; }; + Microsoft.Terminal.TerminalConnection.ITerminalConnection Connection; String[] Commandline; - String CurrentDirectory { get; }; - UInt32 ShowWindowCommand { get; }; - String CurrentEnvironment { get; }; + String CurrentDirectory; + UInt32 ShowWindowCommand; + String CurrentEnvironment; }; enum MonitorBehavior diff --git a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw index 07d4509b7c..4f6031326f 100644 --- a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw @@ -270,13 +270,9 @@ Registerkarte kopieren - - Profil mit einem ungültigen "backgroundImage" gefunden. Dieses Profil hat standardmäßig kein Hintergrundbild. Stellen Sie sicher, dass beim Festlegen eines "backgroundImage" der Wert ein gültiger Dateipfad zu einem Bild ist. - {Locked="\"backgroundImage\""} - - - Profil mit einem ungültigen "icon" gefunden. Dieses Profil hat standardmäßig kein Symbol. Stellen Sie sicher, dass beim Festlegen eines "icon" der Wert ein gültiger Dateipfad zu einem Bild ist. - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + Mindestens eine in den Einstellungen angegebene Ressource (z. B. icon oder backgroundImage) wurde nicht gefunden. + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. Beim Analysieren Ihrer Tastenzuordnungen wurden Warnungen gefunden: diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 9ff10d00f4..0dd84b19a4 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -271,13 +271,9 @@ Duplicate tab - - Found a profile with an invalid "backgroundImage". Defaulting that profile to have no background image. Make sure that when setting a "backgroundImage", the value is a valid file path to an image. - {Locked="\"backgroundImage\""} - - - Found a profile with an invalid "icon". Defaulting that profile to have no icon. Make sure that when setting an "icon", the value is a valid file path to an image. - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + One or more resources (such as icon or backgroundImage) specified in your settings could not be found. + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. Warnings were found while parsing your keybindings: diff --git a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw index 3ef00fe6a1..0665b781c7 100644 --- a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw @@ -267,13 +267,9 @@ Duplicar pestaña - - Se encontró un perfil con un "backgroundImage" no válido. Si se predetermina que ese perfil no tiene imagen de fondo. Asegúrese de que al establecer "backgroundImage", el valor sea una ruta de acceso de archivo válida a una imagen. - {Locked="\"backgroundImage\""} - - - Se encontró un perfil con un "icon" no válido. Estableciendo ese perfil para no tener icono. Asegúrese de que, al establecer un "icon", el valor es una ruta de acceso de archivo válida a una imagen. - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + No se encontraron uno o varios recursos (como icon o backgroundImage) especificados en la configuración. + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. Se encontraron advertencias al analizar los enlaces de teclado: diff --git a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw index 6d897738c0..d8db10aae5 100644 --- a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw @@ -267,13 +267,9 @@ Dupliquer l’onglet - - Profil détecté avec une "backgroundImage" non valide. Par défaut, ce profil ne possède pas d’image d’arrière-plan. Assurez-vous que lorsque vous définissez une "backgroundImage", la valeur est un chemin d’accès de fichier valide vers une image. - {Locked="\"backgroundImage\""} - - - Profil détecté avec une "icon" non valide. Par défaut, ce profil ne possède pas d’icône. Assurez-vous que lorsque vous définissez une "icon", la valeur est un chemin d’accès de fichier valide vers une image. - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + Une ou plusieurs ressources (telles que icon ou backgroundImage) spécifiées dans vos paramètres sont introuvables. + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. Des avertissements ont été détectés lors de l’analyse de vos combinaisons de touches : diff --git a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw index c8ae13a833..95f101c618 100644 --- a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw @@ -267,13 +267,9 @@ Duplica scheda - - È stato trovato un profilo con un "backgroundImage" non valido. Impostazione predefinita per il profilo non è disponibile un'immagine di sfondo. Accertarsi che quando si imposta un "backgroundImage", il valore è un percorso di file valido per un'immagine. - {Locked="\"backgroundImage\""} - - - Trovato un profilo con "icon" non valida. Impostare il profilo senza icon. Assicurarsi che, quando si imposta una "icon", il valore abbia un percorso file valido per un'immagine. - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + Impossibile trovare una o più risorse, ad esempio icon o backgroundImage, specificate nelle impostazioni. + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. Sono stati trovati avvisi durante l'analisi delle associazioni di tasti: diff --git a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw index 462193c2ce..af8f44efc0 100644 --- a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw @@ -268,13 +268,9 @@ タブを複製する - - 無効な "backgroundImage" を持つプロファイルが見つかりました。既定では、そのプロファイルに背景画像はありません。"backgroundImage" を設定するときに、値が画像への有効なファイル パスとなっていることをご確認ください。 - {Locked="\"backgroundImage\""} - - - 無効な "icon" を持つプロファイルが見つかりました。既定では、そのプロファイルにアイコンはありません。"icon" を設定するときに、値が画像への有効なファイル パスとなっていることをご確認ください。 - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + 設定で指定された 1 つ以上のリソース (icon や backgroundImage など) が見つかりませんでした。 + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. キー バインドの解析中に警告が検出されました: diff --git a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw index 14fdffdaa8..0fbe737c10 100644 --- a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw @@ -267,13 +267,9 @@ 탭 복제 - - 잘못된 "backgroundImage" 프로필을 찾았습니다. 해당 프로필을 배경 이미지가 없는 기본값으로 설정합니다. "backgroundImage"를 설정할 때 값이 이미지에 대한 유효한 파일 경로인지 확인합니다. - {Locked="\"backgroundImage\""} - - - 잘못된 "icon"이 있는 프로필을 발견했습니다. 해당 프로필에 아이콘이 없도록 기본값을 설정합니다. "icon" 설정 시 값이 이미지에 대한 올바른 파일 경로인지 확인합니다. - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + 설정에 지정된 하나 이상의 리소스(예: icon 또는 backgroundImage)를 찾을 수 없습니다. + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. 키 바인딩 구문을 분석하는 동안 경고를 발견했습니다. diff --git a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw index b75bf1c3b1..7783aa3dac 100644 --- a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw @@ -267,13 +267,9 @@ Duplicar guia - - Foi encontrado um perfil com um "backgroundImage" inválido. O perfil deve ser o padrão para que não haja nenhuma imagem de tela de fundo. Certifique-se de que, ao definir um "backgroundImage", o valor é um caminho de arquivo válido para uma imagem. - {Locked="\"backgroundImage\""} - - - Foi encontrado um perfil com um "icon" inválido. Padronize esse perfil para ele não ter ícone. Certifique-se de que, ao definir um "icon", o valor seja um caminho de arquivo válido para uma imagem. - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + Um ou mais recursos (como icon ou backgroundImage) especificados em suas configurações não foram encontrados. + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. Os avisos foram encontrados durante a análise das suas ligações de teclas: diff --git a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw index f737463a65..26d019c955 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw @@ -271,13 +271,9 @@ Ďϋφľіčάтέ τàв !!! - - ₣σúŋδ ą φѓοƒĩļé ẃϊţħ äй ïηνàĺìď "backgroundImage". Đēƒãųŀŧϊпğ ťнªт φѓőƒĭļè το нªνе πō ьąçќġгθúпδ ιмãġė. Маĸē śμѓē ŧћäţ ẁђēή šêťτϊлġ å "backgroundImage", ţĥě νаłųё ïŝ ά νάľîď ƒĩŀê φąťħ ţŏ άń ΐмąġė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - {Locked="\"backgroundImage\""} - - - ₣ǿũиđ à рřöƒϊℓз ŵĩţн аñ įņνàŀїδ "icon". Ðěƒаúľτīŋğ ţħаτ ρřόƒìŀё тб ђâνє пǿ íčой. Мàĸë ŝùřë ŧĥаţ ωĥĕл ŝеτŧīлĝ ăй "icon", τħε νāłϋë ïŝ ă νàľīđ ƒïŀè рªтн ţő äи ïмäģё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + Ωňę бг mθгě яėŝǿüґсėş (šυćн âş icon ōя backgroundImage) ŝрěçìƒįєð ίπ ýőūŕ ŝėтťīлġš ċòŭĺð йöŧ вέ ƒòúпď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. Щαѓńΐňģš ώĕřе ƒбŭπδ ώħīļë рăяşìⁿġ ўσυŕ κёỳвĩиðīήġş: !!! !!! !!! !!! !!! diff --git a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw index f737463a65..26d019c955 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw @@ -271,13 +271,9 @@ Ďϋφľіčάтέ τàв !!! - - ₣σúŋδ ą φѓοƒĩļé ẃϊţħ äй ïηνàĺìď "backgroundImage". Đēƒãųŀŧϊпğ ťнªт φѓőƒĭļè το нªνе πō ьąçќġгθúпδ ιмãġė. Маĸē śμѓē ŧћäţ ẁђēή šêťτϊлġ å "backgroundImage", ţĥě νаłųё ïŝ ά νάľîď ƒĩŀê φąťħ ţŏ άń ΐмąġė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - {Locked="\"backgroundImage\""} - - - ₣ǿũиđ à рřöƒϊℓз ŵĩţн аñ įņνàŀїδ "icon". Ðěƒаúľτīŋğ ţħаτ ρřόƒìŀё тб ђâνє пǿ íčой. Мàĸë ŝùřë ŧĥаţ ωĥĕл ŝеτŧīлĝ ăй "icon", τħε νāłϋë ïŝ ă νàľīđ ƒïŀè рªтн ţő äи ïмäģё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + Ωňę бг mθгě яėŝǿüґсėş (šυćн âş icon ōя backgroundImage) ŝрěçìƒįєð ίπ ýőūŕ ŝėтťīлġš ċòŭĺð йöŧ вέ ƒòúпď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. Щαѓńΐňģš ώĕřе ƒбŭπδ ώħīļë рăяşìⁿġ ўσυŕ κёỳвĩиðīήġş: !!! !!! !!! !!! !!! diff --git a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw index f737463a65..26d019c955 100644 --- a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw @@ -271,13 +271,9 @@ Ďϋφľіčάтέ τàв !!! - - ₣σúŋδ ą φѓοƒĩļé ẃϊţħ äй ïηνàĺìď "backgroundImage". Đēƒãųŀŧϊпğ ťнªт φѓőƒĭļè το нªνе πō ьąçќġгθúпδ ιмãġė. Маĸē śμѓē ŧћäţ ẁђēή šêťτϊлġ å "backgroundImage", ţĥě νаłųё ïŝ ά νάľîď ƒĩŀê φąťħ ţŏ άń ΐмąġė. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ! - {Locked="\"backgroundImage\""} - - - ₣ǿũиđ à рřöƒϊℓз ŵĩţн аñ įņνàŀїδ "icon". Ðěƒаúľτīŋğ ţħаτ ρřόƒìŀё тб ђâνє пǿ íčой. Мàĸë ŝùřë ŧĥаţ ωĥĕл ŝеτŧīлĝ ăй "icon", τħε νāłϋë ïŝ ă νàľīđ ƒïŀè рªтн ţő äи ïмäģё. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + Ωňę бг mθгě яėŝǿüґсėş (šυćн âş icon ōя backgroundImage) ŝрěçìƒįєð ίπ ýőūŕ ŝėтťīлġš ċòŭĺð йöŧ вέ ƒòúпď. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. Щαѓńΐňģš ώĕřе ƒбŭπδ ώħīļë рăяşìⁿġ ўσυŕ κёỳвĩиðīήġş: !!! !!! !!! !!! !!! diff --git a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw index 4bff030fdd..2601c96140 100644 --- a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw @@ -267,13 +267,9 @@ Дублировать вкладку - - Найден профиль с недопустимым объектом "backgroundImage". По умолчанию для этого профиля не используется фоновое изображение. Убедитесь, что значение, заданное для "backgroundImage", является допустимым путем файла к изображению. - {Locked="\"backgroundImage\""} - - - Найден профиль с недопустимым объектом "icon". По умолчанию для этого профиля не используется значок. Убедитесь, что значение, заданное для "icon", является допустимым путем файла к изображению. - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + Не удалось найти один или несколько ресурсов (icon или backgroundImage), указанных в параметрах. + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. При анализе настраиваемых сочетаний клавиш найдены предупреждения: diff --git a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw index d5935358d5..d6756f762c 100644 --- a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw @@ -267,13 +267,9 @@ 复制标签页 - - 找到一个具有无效 "backgroundImage" 的配置文件。将该配置文件设置为默认设置为不包含背景图像。请确保在设置 "backgroundImage" 时,该值是指向图像的有效文件路径。 - {Locked="\"backgroundImage\""} - - - 找到一个带有无效 "icon" 的配置文件。将该配置文件默认为无图标。确保设置 "icon" 时,该值是图像的有效文件路径。 - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + 找不到设置中指定的一个或多个资源 (,如 icon 或 backgroundImage)。 + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. 分析键绑定时发现警告: diff --git a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw index 26689ad636..d285d75c60 100644 --- a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw @@ -267,13 +267,9 @@ 複製索引標籤 - - 找到具有無效 "backgroundImage" 的設定檔。將該設定檔的預設值設為沒有背景影像。請確定設定 "backgroundImage" 時,該值是影像的有效檔案路徑。 - {Locked="\"backgroundImage\""} - - - 已發現具有無效 "icon" 的設定檔。將該設定檔預設為無圖示。設定 "icon" 時,請確認值是有效的影像檔案路徑。 - {Locked="\"icon\""} The word "icon" in quotes is locked, the word icon OUTSIDE of quotes should be localized. + + 找不到一或多個資源 (,例如在您的設定中指定的 icon 或 backgroundImage)。 + {Locked="icon","backgroundImage"} Indicates that something has gone wrong while reading a user's settings. 剖析金鑰繫結時發現警告: diff --git a/src/cascadia/TerminalApp/SettingsLoadEventArgs.h b/src/cascadia/TerminalApp/SettingsLoadEventArgs.h index e094810784..f9436f95ae 100644 --- a/src/cascadia/TerminalApp/SettingsLoadEventArgs.h +++ b/src/cascadia/TerminalApp/SettingsLoadEventArgs.h @@ -12,14 +12,14 @@ namespace winrt::TerminalApp::implementation WINRT_PROPERTY(bool, Reload, false); WINRT_PROPERTY(uint64_t, Result, S_OK); WINRT_PROPERTY(winrt::hstring, ExceptionText, L""); - WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVector, Warnings, nullptr); + WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVectorView, Warnings, nullptr); WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::CascadiaSettings, NewSettings, nullptr); public: SettingsLoadEventArgs(bool reload, uint64_t result, winrt::hstring exceptionText, - winrt::Windows::Foundation::Collections::IVector warnings, + winrt::Windows::Foundation::Collections::IVectorView warnings, Microsoft::Terminal::Settings::Model::CascadiaSettings newSettings) : _Reload{ reload }, _Result{ result }, diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp b/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp index 28cb64b5aa..fe03a94f78 100644 --- a/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp +++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp @@ -27,7 +27,7 @@ namespace winrt::TerminalApp::implementation // Arguments: // - actionAndArgs: the ShortcutAction and associated args to raise an event for. // Return Value: - // - true if we handled the event was handled, else false. + // - true if the event was handled, else false. bool ShortcutActionDispatch::DoAction(const winrt::Windows::Foundation::IInspectable& sender, const ActionAndArgs& actionAndArgs) { diff --git a/src/cascadia/TerminalApp/SnippetsPaneContent.cpp b/src/cascadia/TerminalApp/SnippetsPaneContent.cpp index 415e5d8201..d2dd9fb03e 100644 --- a/src/cascadia/TerminalApp/SnippetsPaneContent.cpp +++ b/src/cascadia/TerminalApp/SnippetsPaneContent.cpp @@ -32,6 +32,7 @@ namespace winrt::TerminalApp::implementation void SnippetsPaneContent::_updateFilteredCommands() { const auto& queryString = _filterBox().Text(); + auto pattern = std::make_shared(fzf::matcher::ParsePattern(queryString)); // DON'T replace the itemSource here. If you do, it'll un-expand all the // nested items the user has expanded. Instead, just update the filter. @@ -39,7 +40,7 @@ namespace winrt::TerminalApp::implementation for (const auto& t : _allTasks) { auto impl = winrt::get_self(t); - impl->UpdateFilter(queryString); + impl->UpdateFilter(pattern); } } diff --git a/src/cascadia/TerminalApp/SnippetsPaneContent.h b/src/cascadia/TerminalApp/SnippetsPaneContent.h index c469e83fcc..9daf927665 100644 --- a/src/cascadia/TerminalApp/SnippetsPaneContent.h +++ b/src/cascadia/TerminalApp/SnippetsPaneContent.h @@ -6,7 +6,7 @@ #include "FilteredTask.g.h" #include "BasicPaneEvents.h" #include "FilteredCommand.h" -#include "ActionPaletteItem.h" +#include "CommandPaletteItems.h" #include namespace winrt::TerminalApp::implementation @@ -62,7 +62,7 @@ namespace winrt::TerminalApp::implementation FilteredTask(const winrt::Microsoft::Terminal::Settings::Model::Command& command) { - _filteredCommand = winrt::make_self(winrt::make(command, winrt::hstring{})); + _filteredCommand = winrt::make_self(winrt::make(command, winrt::hstring{})); _command = command; // The Children() method must always return a non-null vector @@ -77,13 +77,14 @@ namespace winrt::TerminalApp::implementation } } - void UpdateFilter(const winrt::hstring& filter) + void UpdateFilter(std::shared_ptr pattern) { - _filteredCommand->UpdateFilter(filter); + _pattern = std::move(pattern); + _filteredCommand->UpdateFilter(_pattern); for (const auto& c : _children) { auto impl = winrt::get_self(c); - impl->UpdateFilter(filter); + impl->UpdateFilter(_pattern); } PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"Visibility" }); @@ -91,14 +92,13 @@ namespace winrt::TerminalApp::implementation winrt::hstring Input() { - if (const auto& actionItem{ _filteredCommand->Item().try_as() }) + // **SAFETY GUARANTEE** We constructed this filtered command ourselves; we know what's inside it. + const auto actionItem{ winrt::get_self(_filteredCommand->Item()) }; + if (const auto& command{ actionItem->Command() }) { - if (const auto& command{ actionItem.Command() }) + if (const auto& sendInput{ command.ActionAndArgs().Args().try_as() }) { - if (const auto& sendInput{ command.ActionAndArgs().Args().try_as() }) - { - return winrt::hstring{ til::visualize_nonspace_control_codes(std::wstring{ sendInput.Input() }) }; - } + return winrt::hstring{ til::visualize_nonspace_control_codes(std::wstring{ sendInput.Input() }) }; } } return winrt::hstring{}; @@ -108,6 +108,7 @@ namespace winrt::TerminalApp::implementation bool HasChildren() { return _children.Size() > 0; } winrt::Microsoft::Terminal::Settings::Model::Command Command() { return _command; } winrt::TerminalApp::FilteredCommand FilteredCommand() { return *_filteredCommand; } + std::shared_ptr _pattern; int32_t Row() { return HasChildren() ? 2 : 1; } // See the BODGY comment in the .XAML for explanation @@ -117,7 +118,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Visibility Visibility() { // Is there no filter, or do we match it? - if (_filteredCommand->Filter().empty() || _filteredCommand->Weight() > 0) + if ((!_pattern || _pattern->terms.empty() || _filteredCommand->Weight() > 0)) { return winrt::Windows::UI::Xaml::Visibility::Visible; } diff --git a/src/cascadia/TerminalApp/SnippetsPaneContent.xaml b/src/cascadia/TerminalApp/SnippetsPaneContent.xaml index b47187f5e6..b6d054ae52 100644 --- a/src/cascadia/TerminalApp/SnippetsPaneContent.xaml +++ b/src/cascadia/TerminalApp/SnippetsPaneContent.xaml @@ -170,8 +170,9 @@ + Text="{x:Bind FilteredCommand.Item.Name, Mode=OneWay}" /> - - + @@ -98,17 +98,13 @@ PaletteItemTemplateSelector.idl Code - - - TabBase.idl + + + Tab.idl - TaskbarState.idl - - TerminalTab.idl - TerminalPage.xaml Code @@ -127,9 +123,8 @@ TabHeaderControl.xaml - HighlightedTextControl.xaml + HighlightedTextControl.idl - ColorPickupFlyout.xaml @@ -138,7 +133,7 @@ - + ShortcutActionDispatch.idl @@ -191,11 +186,10 @@ - - + @@ -208,17 +202,13 @@ PaletteItemTemplateSelector.idl Code - - - TabBase.idl + + Tab.idl - + TaskbarState.idl - - TerminalTab.idl - TerminalPage.xaml Code @@ -245,9 +235,8 @@ TabHeaderControl.xaml - HighlightedTextControl.xaml + HighlightedTextControl.idl - ColorPickupFlyout.xaml @@ -257,9 +246,6 @@ - - NotUsing - Create @@ -318,8 +304,6 @@ - - AboutDialog.xaml @@ -330,7 +314,7 @@ Designer - + @@ -339,10 +323,8 @@ MinMaxCloseControl.xaml Code - - + - TerminalPage.xaml Code @@ -363,10 +345,8 @@ Code - HighlightedTextControl.xaml Code - ColorPickupFlyout.xaml Code diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters index 12b3fa2add..d082b2a054 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters @@ -20,26 +20,16 @@ - commandPalette - + commandPalette - - commandPalette - - - commandPalette - - - commandPalette - - - highlightedText + + fzf @@ -57,25 +47,21 @@ - commandPalette - + commandPalette - + commandPalette - - commandPalette + + fzf - - commandPalette - - - highlightedText + + fzf @@ -93,20 +79,14 @@ settings - - tab - commandPalette - - highlightedText - tab - + @@ -135,19 +115,10 @@ commandPalette - + commandPalette - - commandPalette - - - commandPalette - - - commandPalette - - + highlightedText @@ -176,6 +147,9 @@ {e490f626-547d-4b5b-b22d-c6d33c9e3210} + + {e4588ff4-c80a-40f7-be57-3e81f570a93d} + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 702b4a36ee..507d29392b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -6,25 +6,24 @@ #include "TerminalPage.h" #include -#include -#include #include +#include -#include "../../types/inc/utils.hpp" #include "App.h" -#include "ColorHelper.h" #include "DebugTapConnection.h" -#include "SettingsPaneContent.h" -#include "ScratchpadContent.h" -#include "SnippetsPaneContent.h" #include "MarkdownPaneContent.h" -#include "TabRowControl.h" #include "Remoting.h" +#include "ScratchpadContent.h" +#include "SettingsPaneContent.h" +#include "SnippetsPaneContent.h" +#include "TabRowControl.h" +#include "../../types/inc/ColorFix.hpp" +#include "../../types/inc/utils.hpp" -#include "TerminalPage.g.cpp" +#include "LaunchPositionRequest.g.cpp" #include "RenameWindowRequestedArgs.g.cpp" #include "RequestMoveContentArgs.g.cpp" -#include "LaunchPositionRequest.g.cpp" +#include "TerminalPage.g.cpp" using namespace winrt; using namespace winrt::Microsoft::Management::Deployment; @@ -57,11 +56,126 @@ namespace winrt using VirtualKeyModifiers = Windows::System::VirtualKeyModifiers; } +namespace clipboard +{ + wil::unique_close_clipboard_call open(HWND hwnd) + { + bool success = false; + + // OpenClipboard may fail to acquire the internal lock --> retry. + for (DWORD sleep = 10;; sleep *= 2) + { + if (OpenClipboard(hwnd)) + { + success = true; + break; + } + // 10 iterations + if (sleep > 10000) + { + break; + } + Sleep(sleep); + } + + return wil::unique_close_clipboard_call{ success }; + } + + void write(wil::zwstring_view text, std::string_view html, std::string_view rtf) + { + static const auto regular = [](const UINT format, const void* src, const size_t bytes) { + wil::unique_hglobal handle{ THROW_LAST_ERROR_IF_NULL(GlobalAlloc(GMEM_MOVEABLE, bytes)) }; + + const auto locked = GlobalLock(handle.get()); + memcpy(locked, src, bytes); + GlobalUnlock(handle.get()); + + THROW_LAST_ERROR_IF_NULL(SetClipboardData(format, handle.get())); + handle.release(); + }; + static const auto registered = [](const wchar_t* format, const void* src, size_t bytes) { + const auto id = RegisterClipboardFormatW(format); + if (!id) + { + LOG_LAST_ERROR(); + return; + } + regular(id, src, bytes); + }; + + EmptyClipboard(); + + if (!text.empty()) + { + // As per: https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats + // CF_UNICODETEXT: [...] A null character signals the end of the data. + // --> We add +1 to the length. This works because .c_str() is null-terminated. + regular(CF_UNICODETEXT, text.c_str(), (text.size() + 1) * sizeof(wchar_t)); + } + + if (!html.empty()) + { + registered(L"HTML Format", html.data(), html.size()); + } + + if (!rtf.empty()) + { + registered(L"Rich Text Format", rtf.data(), rtf.size()); + } + } + + winrt::hstring read() + { + // This handles most cases of pasting text as the OS converts most formats to CF_UNICODETEXT automatically. + if (const auto handle = GetClipboardData(CF_UNICODETEXT)) + { + const wil::unique_hglobal_locked lock{ handle }; + const auto str = static_cast(lock.get()); + if (!str) + { + return {}; + } + + const auto maxLen = GlobalSize(handle) / sizeof(wchar_t); + const auto len = wcsnlen(str, maxLen); + return winrt::hstring{ str, gsl::narrow_cast(len) }; + } + + // We get CF_HDROP when a user copied a file with Ctrl+C in Explorer and pastes that into the terminal (among others). + if (const auto handle = GetClipboardData(CF_HDROP)) + { + const wil::unique_hglobal_locked lock{ handle }; + const auto drop = static_cast(lock.get()); + if (!drop) + { + return {}; + } + + const auto cap = DragQueryFileW(drop, 0, nullptr, 0); + if (cap == 0) + { + return {}; + } + + auto buffer = winrt::impl::hstring_builder{ cap }; + const auto len = DragQueryFileW(drop, 0, buffer.data(), cap + 1); + if (len == 0) + { + return {}; + } + + return buffer.to_hstring(); + } + + return {}; + } +} // namespace clipboard + namespace winrt::TerminalApp::implementation { TerminalPage::TerminalPage(TerminalApp::WindowProperties properties, const TerminalApp::ContentManager& manager) : - _tabs{ winrt::single_threaded_observable_vector() }, - _mruTabs{ winrt::single_threaded_observable_vector() }, + _tabs{ winrt::single_threaded_observable_vector() }, + _mruTabs{ winrt::single_threaded_observable_vector() }, _manager{ manager }, _hostingHwnd{}, _WindowProperties{ std::move(properties) } @@ -85,9 +199,9 @@ namespace winrt::TerminalApp::implementation // GH#13211 - if we haven't yet set the owning hwnd, reparent all the controls now. for (const auto& tab : _tabs) { - if (auto terminalTab{ _GetTerminalTabImpl(tab) }) + if (auto tabImpl{ _GetTabImpl(tab) }) { - terminalTab->GetRootPane()->WalkTree([&](auto&& pane) { + tabImpl->GetRootPane()->WalkTree([&](auto&& pane) { if (const auto& term{ pane->GetTerminalControl() }) { term.OwningHwnd(reinterpret_cast(hwnd)); @@ -238,6 +352,14 @@ namespace winrt::TerminalApp::implementation _newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) { if (auto page{ weakThis.get() }) { + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuDefaultButtonClicked", + TraceLoggingDescription("Event emitted when the default button from the new tab split button is invoked"), + TraceLoggingValue(page->NumberOfTabs(), "TabCount", "The count of tabs currently opened in this window"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + page->_OpenNewTerminalViaDropdown(NewTerminalArgs()); } }); @@ -293,12 +415,11 @@ namespace winrt::TerminalApp::implementation // - true if we're not elevated but all relevant pane-spawning actions are elevated bool TerminalPage::ShouldImmediatelyHandoffToElevated(const CascadiaSettings& settings) const { - // GH#12267: Don't forget about defterm handoff here. If we're being - // created for embedding, then _yea_, we don't need to handoff to an - // elevated window. - if (_startupActions.empty() || IsRunningElevated() || _shouldStartInboundListener) + if (_startupActions.empty() || _startupConnection || IsRunningElevated()) { - // there aren't startup actions, or we're elevated. In that case, go for it. + // No point in handing off if we got no startup actions, or we're already elevated. + // Also, we shouldn't need to elevate handoff ConPTY connections. + assert(!_startupConnection); return false; } @@ -488,46 +609,16 @@ namespace winrt::TerminalApp::implementation { _startupState = StartupState::InStartup; - ProcessStartupActions(std::move(_startupActions), true); - - // If we were told that the COM server needs to be started to listen for incoming - // default application connections, start it now. - // This MUST be done after we've registered the event listener for the new connections - // or the COM server might start receiving requests on another thread and dispatch - // them to nowhere. - _StartInboundListener(); - } - } - - // Routine Description: - // - Will start the listener for inbound console handoffs if we have already determined - // that we should do so. - // NOTE: Must be after TerminalPage::_OnNewConnection has been connected up. - // Arguments: - // - - Looks at _shouldStartInboundListener - // Return Value: - // - - May fail fast if setup fails as that would leave us in a weird state. - void TerminalPage::_StartInboundListener() - { - if (_shouldStartInboundListener) - { - _shouldStartInboundListener = false; - - // Hook up inbound connection event handler - _newConnectionRevoker = ConptyConnection::NewConnection(winrt::auto_revoke, { this, &TerminalPage::_OnNewConnection }); - - try + if (_startupConnection) { - winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::StartInboundListener(); + CreateTabFromConnection(std::move(_startupConnection)); } - // If we failed to start the listener, it will throw. - // We don't want to fail fast here because if a peasant has some trouble with - // starting the listener, we don't want it to crash and take all its tabs down - // with it. - catch (...) + else if (!_startupActions.empty()) { - LOG_CAUGHT_EXCEPTION(); + ProcessStartupActions(std::move(_startupActions)); } + + _CompleteInitialization(); } } @@ -545,7 +636,7 @@ namespace winrt::TerminalApp::implementation // nt -d .` from inside another directory to work as expected. // Return Value: // - - safe_void_coroutine TerminalPage::ProcessStartupActions(std::vector actions, const bool initial, const winrt::hstring cwd, const winrt::hstring env) + safe_void_coroutine TerminalPage::ProcessStartupActions(std::vector actions, const winrt::hstring cwd, const winrt::hstring env) { const auto strong = get_strong(); @@ -569,39 +660,76 @@ namespace winrt::TerminalApp::implementation _WindowProperties.VirtualEnvVars(env); } + // The current TerminalWindow & TerminalPage architecture is rather instable + // and fails to start up if the first tab isn't created synchronously. + // + // While that's a fair assumption in on itself, simultaneously WinUI will + // not assign tab contents a size if they're not shown at least once, + // which we need however in order to initialize ControlCore with a size. + // + // So, we do two things here: + // * DO NOT suspend if this is the first tab. + // * DO suspend between the creation of panes (or tabs) in order to allow + // WinUI to layout the new controls and for ControlCore to get a size. + // + // This same logic is also applied to CreateTabFromConnection. + // + // See GH#13136. + auto suspend = _tabs.Size() > 0; + for (size_t i = 0; i < actions.size(); ++i) { - if (i != 0) + if (suspend) { - // Each action may rely on the XAML layout of a preceding action. - // Most importantly, this is the case for the combination of NewTab + SplitPane, - // as the former appears to only have a layout size after at least 1 resume_foreground, - // while the latter relies on that information. This is also why it uses Low priority. - // - // Curiously, this does not seem to be required when using startupActions, but only when - // tearing out a tab (this currently creates a new window with injected startup actions). - // This indicates that this is really more of an architectural issue and not a fundamental one. co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Low); } _actionDispatch->DoAction(actions[i]); + suspend = true; } // GH#6586: now that we're done processing all startup commands, // focus the active control. This will work as expected for both // commandline invocations and for `wt` action invocations. - if (const auto& terminalTab{ _GetFocusedTabImpl() }) + if (const auto& tabImpl{ _GetFocusedTabImpl() }) { - if (const auto& content{ terminalTab->GetActiveContent() }) + if (const auto& content{ tabImpl->GetActiveContent() }) { content.Focus(FocusState::Programmatic); } } + } - if (initial) + safe_void_coroutine TerminalPage::CreateTabFromConnection(ITerminalConnection connection) + { + const auto strong = get_strong(); + + // This is the exact same logic as in ProcessStartupActions. + if (_tabs.Size() > 0) { - _CompleteInitialization(); + co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Low); } + + NewTerminalArgs newTerminalArgs; + + if (const auto conpty = connection.try_as()) + { + newTerminalArgs.Commandline(conpty.Commandline()); + newTerminalArgs.TabTitle(conpty.StartingTitle()); + } + + // GH #12370: We absolutely cannot allow a defterm connection to + // auto-elevate. Defterm doesn't work for elevated scenarios in the + // first place. If we try accepting the connection, the spawning an + // elevated version of the Terminal with that profile... that's a + // recipe for disaster. We won't ever open up a tab in this window. + newTerminalArgs.Elevate(false); + + const auto newPane = _MakePane(newTerminalArgs, nullptr, std::move(connection)); + newPane->WalkTree([](const auto& pane) { + pane->FinalizeConfigurationGivenDefault(); + }); + _CreateNewTabFromPane(newPane); } // Method Description: @@ -629,7 +757,7 @@ namespace winrt::TerminalApp::implementation // GH#12267: Make sure that we don't instantly close ourselves when // we're readying to accept a defterm connection. In that case, we don't // have a tab yet, but will once we're initialized. - if (_tabs.Size() == 0 && !_shouldStartInboundListener && !_isEmbeddingInboundListener) + if (_tabs.Size() == 0) { CloseWindowRequested.raise(*this, nullptr); co_return; @@ -848,14 +976,36 @@ namespace winrt::TerminalApp::implementation // Since the previous focus location might be discarded in the background, // e.g., the command palette will be dismissed by the menu, // and then closing the fly-out will move the focus to wrong location. - newTabFlyout.Opening([this](auto&&, auto&&) { - _FocusCurrentTab(true); + newTabFlyout.Opening([weakThis{ get_weak() }](auto&&, auto&&) { + if (auto page{ weakThis.get() }) + { + page->_FocusCurrentTab(true); + + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuOpened", + TraceLoggingDescription("Event emitted when the new tab menu is opened"), + TraceLoggingValue(page->NumberOfTabs(), "TabCount", "The Count of tabs currently opened in this window"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + } }); // Necessary for fly-out sub items to get focus on a tab before collapsing. Related to #15049 - newTabFlyout.Closing([this](auto&&, auto&&) { - if (!_commandPaletteIs(Visibility::Visible)) + newTabFlyout.Closing([weakThis{ get_weak() }](auto&&, auto&&) { + if (auto page{ weakThis.get() }) { - _FocusCurrentTab(true); + if (!page->_commandPaletteIs(Visibility::Visible)) + { + page->_FocusCurrentTab(true); + } + + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuClosed", + TraceLoggingDescription("Event emitted when the new tab menu is closed"), + TraceLoggingValue(page->NumberOfTabs(), "TabCount", "The Count of tabs currently opened in this window"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } }); _newTabButton.Flyout(newTabFlyout); @@ -921,7 +1071,7 @@ namespace winrt::TerminalApp::implementation auto folderItem = WUX::Controls::MenuFlyoutSubItem{}; folderItem.Text(folderEntry.Name()); - auto icon = _CreateNewTabFlyoutIcon(folderEntry.Icon()); + auto icon = _CreateNewTabFlyoutIcon(folderEntry.Icon().Resolved()); folderItem.Icon(icon); for (const auto& folderEntryItem : folderEntryItems) @@ -971,7 +1121,7 @@ namespace winrt::TerminalApp::implementation break; } - auto profileItem = _CreateNewTabFlyoutProfile(profileEntry.Profile(), profileEntry.ProfileIndex(), profileEntry.Icon()); + auto profileItem = _CreateNewTabFlyoutProfile(profileEntry.Profile(), profileEntry.ProfileIndex(), profileEntry.Icon().Resolved()); items.push_back(profileItem); break; } @@ -981,7 +1131,7 @@ namespace winrt::TerminalApp::implementation const auto actionId = actionEntry.ActionId(); if (_settings.ActionMap().GetActionByID(actionId)) { - auto actionItem = _CreateNewTabFlyoutAction(actionId, actionEntry.Icon()); + auto actionItem = _CreateNewTabFlyoutAction(actionId, actionEntry.Icon().Resolved()); items.push_back(actionItem); } @@ -1020,7 +1170,7 @@ namespace winrt::TerminalApp::implementation // If a custom icon path has been specified, set it as the icon for // this flyout item. Otherwise, if an icon is set for this profile, set that icon // for this flyout item. - const auto& iconPath = iconPathOverride.empty() ? profile.EvaluatedIcon() : iconPathOverride; + const auto& iconPath = iconPathOverride.empty() ? profile.Icon().Resolved() : iconPathOverride; if (!iconPath.empty()) { const auto icon = _CreateNewTabFlyoutIcon(iconPath); @@ -1061,6 +1211,15 @@ namespace winrt::TerminalApp::implementation profileMenuItem.Click([profileIndex, weakThis{ get_weak() }](auto&&, auto&&) { if (auto page{ weakThis.get() }) { + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuItemClicked", + TraceLoggingDescription("Event emitted when an item from the new tab menu is invoked"), + TraceLoggingValue(page->NumberOfTabs(), "TabCount", "The count of tabs currently opened in this window"), + TraceLoggingValue("Profile", "ItemType", "The type of item that was clicked in the new tab menu"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + NewTerminalArgs newTerminalArgs{ profileIndex }; page->_OpenNewTerminalViaDropdown(newTerminalArgs); } @@ -1097,7 +1256,7 @@ namespace winrt::TerminalApp::implementation // If a custom icon path has been specified, set it as the icon for // this flyout item. Otherwise, if an icon is set for this action, set that icon // for this flyout item. - const auto& iconPath = iconPathOverride.empty() ? action.IconPath() : iconPathOverride; + const auto& iconPath = iconPathOverride.empty() ? action.Icon().Resolved() : iconPathOverride; if (!iconPath.empty()) { const auto icon = _CreateNewTabFlyoutIcon(iconPath); @@ -1107,6 +1266,15 @@ namespace winrt::TerminalApp::implementation actionMenuItem.Click([action, weakThis{ get_weak() }](auto&&, auto&&) { if (auto page{ weakThis.get() }) { + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuItemClicked", + TraceLoggingDescription("Event emitted when an item from the new tab menu is invoked"), + TraceLoggingValue(page->NumberOfTabs(), "TabCount", "The count of tabs currently opened in this window"), + TraceLoggingValue("Action", "ItemType", "The type of item that was clicked in the new tab menu"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + page->_actionDispatch->DoAction(action.ActionAndArgs()); } }); @@ -1168,6 +1336,7 @@ namespace winrt::TerminalApp::implementation const auto dispatchToElevatedWindow = ctrlPressed && !IsRunningElevated(); + auto sessionType = ""; if ((shiftPressed || dispatchToElevatedWindow) && !debugTap) { // Manually fill in the evaluated profile. @@ -1185,10 +1354,12 @@ namespace winrt::TerminalApp::implementation if (dispatchToElevatedWindow) { _OpenElevatedWT(newTerminalArgs); + sessionType = "ElevatedWindow"; } else { _OpenNewWindow(newTerminalArgs); + sessionType = "Window"; } } else @@ -1207,12 +1378,23 @@ namespace winrt::TerminalApp::implementation SplitDirection::Automatic, 0.5f, newPane); + sessionType = "Pane"; } else { _CreateNewTabFromPane(newPane); + sessionType = "Tab"; } } + + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuCreatedNewTerminalSession", + TraceLoggingDescription("Event emitted when a new terminal was created via the new tab menu"), + TraceLoggingValue(NumberOfTabs(), "NewTabCount", "The count of tabs currently opened in this window"), + TraceLoggingValue(sessionType, "SessionType", "The type of session that was created"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } std::wstring TerminalPage::_evaluatePathForCwd(const std::wstring_view path) @@ -1289,7 +1471,7 @@ namespace winrt::TerminalApp::implementation // // We need to do this here, to ensure we tell the ConptyConnection // the correct starting path. If we're being invoked from another - // terminal instance (e.g. wt -w 0 -d .), then we have switched our + // terminal instance (e.g. `wt -w 0 -d .`), then we have switched our // CWD to the provided path. We should treat the StartingDirectory // as relative to the current CWD. // @@ -1419,6 +1601,30 @@ namespace winrt::TerminalApp::implementation { target = SettingsTarget::DefaultsFile; } + + const auto targetAsString = [&target]() { + switch (target) + { + case SettingsTarget::SettingsFile: + return "SettingsFile"; + case SettingsTarget::DefaultsFile: + return "DefaultsFile"; + case SettingsTarget::SettingsUI: + default: + return "UI"; + } + }(); + + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuItemClicked", + TraceLoggingDescription("Event emitted when an item from the new tab menu is invoked"), + TraceLoggingValue(NumberOfTabs(), "TabCount", "The count of tabs currently opened in this window"), + TraceLoggingValue("Settings", "ItemType", "The type of item that was clicked in the new tab menu"), + TraceLoggingValue(targetAsString, "SettingsTarget", "The target settings file or UI"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + _LaunchSettings(target); } @@ -1430,6 +1636,15 @@ namespace winrt::TerminalApp::implementation auto p = LoadCommandPalette(); p.EnableCommandPaletteMode(CommandPaletteLaunchMode::Action); p.Visibility(Visibility::Visible); + + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuItemClicked", + TraceLoggingDescription("Event emitted when an item from the new tab menu is invoked"), + TraceLoggingValue(NumberOfTabs(), "TabCount", "The count of tabs currently opened in this window"), + TraceLoggingValue("CommandPalette", "ItemType", "The type of item that was clicked in the new tab menu"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } // Method Description: @@ -1442,6 +1657,15 @@ namespace winrt::TerminalApp::implementation const RoutedEventArgs&) { _ShowAboutDialog(); + + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuItemClicked", + TraceLoggingDescription("Event emitted when an item from the new tab menu is invoked"), + TraceLoggingValue(NumberOfTabs(), "TabCount", "The count of tabs currently opened in this window"), + TraceLoggingValue("About", "ItemType", "The type of item that was clicked in the new tab menu"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } // Method Description: @@ -1665,13 +1889,11 @@ namespace winrt::TerminalApp::implementation // TitleChanged event. // Arguments: // - tab: the Tab to update the title for. - void TerminalPage::_UpdateTitle(const TerminalTab& tab) + void TerminalPage::_UpdateTitle(const Tab& tab) { - auto newTabTitle = tab.Title(); - if (tab == _GetFocusedTab()) { - TitleChanged.raise(*this, newTabTitle); + TitleChanged.raise(*this, nullptr); } } @@ -1686,7 +1908,7 @@ namespace winrt::TerminalApp::implementation { term.RaiseNotice({ this, &TerminalPage::_ControlNoticeRaisedHandler }); - // Add an event handler when the terminal wants to paste data from the Clipboard. + term.WriteToClipboard({ get_weak(), &TerminalPage::_copyToClipboard }); term.PasteFromClipboard({ this, &TerminalPage::_PasteFromClipboardHandler }); term.OpenHyperlink({ this, &TerminalPage::_OpenHyperlinkHandler }); @@ -1708,9 +1930,7 @@ namespace winrt::TerminalApp::implementation }); term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler }); - term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler }); - term.WindowSizeChanged({ get_weak(), &TerminalPage::_WindowSizeChanged }); // Don't even register for the event if the feature is compiled off. @@ -1743,13 +1963,13 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Connects event handlers to the TerminalTab for events that we want to + // - Connects event handlers to the Tab for events that we want to // handle. This includes: // * the TitleChanged event, for changing the text of the tab // * the Color{Selected,Cleared} events to change the color of a tab. // Arguments: // - hostingTab: The Tab that's hosting this TermControl instance - void TerminalPage::_RegisterTabEvents(TerminalTab& hostingTab) + void TerminalPage::_RegisterTabEvents(Tab& hostingTab) { auto weakTab{ hostingTab.get_weak() }; auto weakThis{ get_weak() }; @@ -1832,9 +2052,9 @@ namespace winrt::TerminalApp::implementation // to the terminal when no other panes are present (GH#6219) bool TerminalPage::_MoveFocus(const FocusDirection& direction) { - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { - return terminalTab->NavigateFocus(direction); + return tabImpl->NavigateFocus(direction); } return false; } @@ -1848,19 +2068,19 @@ namespace winrt::TerminalApp::implementation // - true if panes were swapped. bool TerminalPage::_SwapPane(const FocusDirection& direction) { - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { _UnZoomIfNeeded(); - return terminalTab->SwapPane(direction); + return tabImpl->SwapPane(direction); } return false; } TermControl TerminalPage::_GetActiveControl() { - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { - return terminalTab->GetActiveTerminalControl(); + return tabImpl->GetActiveTerminalControl(); } return nullptr; } @@ -1967,7 +2187,7 @@ namespace winrt::TerminalApp::implementation for (auto tab : _tabs) { - auto t = winrt::get_self(tab); + auto t = winrt::get_self(tab); auto tabActions = t->BuildStartupActions(serializeBuffer ? BuildStartupKind::PersistAll : BuildStartupKind::PersistLayout); actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end())); } @@ -2062,14 +2282,14 @@ namespace winrt::TerminalApp::implementation // - rowsToScroll: a number of lines to move the viewport. If not provided we will use a system default. void TerminalPage::_Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference& rowsToScroll) { - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { uint32_t realRowsToScroll; if (rowsToScroll == nullptr) { // The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ? - terminalTab->GetActiveTerminalControl().ViewHeight() : + tabImpl->GetActiveTerminalControl().ViewHeight() : _systemRowsToScroll; } else @@ -2078,7 +2298,7 @@ namespace winrt::TerminalApp::implementation realRowsToScroll = rowsToScroll.Value(); } auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll); - terminalTab->Scroll(scrollDelta); + tabImpl->Scroll(scrollDelta); } } @@ -2109,9 +2329,9 @@ namespace winrt::TerminalApp::implementation // specified window instead of moving it in our tab row. if (!windowId.empty()) { - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { - if (const auto pane{ terminalTab->GetActivePane() }) + if (const auto pane{ tabImpl->GetActivePane() }) { auto startupActions = pane->BuildStartupActions(0, 1, BuildStartupKind::MovePane); _DetachPaneFromWindow(pane); @@ -2150,7 +2370,7 @@ namespace winrt::TerminalApp::implementation // tab before its index changes. if (tabIdx < _tabs.Size()) { - auto targetTab = _GetTerminalTabImpl(_tabs.GetAt(tabIdx)); + auto targetTab = _GetTabImpl(_tabs.GetAt(tabIdx)); // if the selected tab is not a host of terminals (e.g. settings) // don't attempt to add a pane to it. if (!targetTab) @@ -2198,15 +2418,12 @@ namespace winrt::TerminalApp::implementation }); } - void TerminalPage::_DetachTabFromWindow(const winrt::com_ptr& tab) + void TerminalPage::_DetachTabFromWindow(const winrt::com_ptr& tab) { - if (const auto terminalTab = tab.try_as()) + // Detach the root pane, which will act like the whole tab got detached. + if (const auto rootPane = tab->GetRootPane()) { - // Detach the root pane, which will act like the whole tab got detached. - if (const auto rootPane = terminalTab->GetRootPane()) - { - _DetachPaneFromWindow(rootPane); - } + _DetachPaneFromWindow(rootPane); } } @@ -2234,7 +2451,7 @@ namespace winrt::TerminalApp::implementation RequestMoveContent.raise(*this, *request); } - bool TerminalPage::_MoveTab(winrt::com_ptr tab, MoveTabArgs args) + bool TerminalPage::_MoveTab(winrt::com_ptr tab, MoveTabArgs args) { if (!tab) { @@ -2302,10 +2519,10 @@ namespace winrt::TerminalApp::implementation // When the tab's active pane changes, we'll want to lookup a new icon // for it. The Title change will be propagated upwards through the tab's // PropertyChanged event handler. - void TerminalPage::_activePaneChanged(winrt::TerminalApp::TerminalTab sender, + void TerminalPage::_activePaneChanged(winrt::TerminalApp::Tab sender, Windows::Foundation::IInspectable /*args*/) { - if (const auto tab{ _GetTerminalTabImpl(sender) }) + if (const auto tab{ _GetTabImpl(sender) }) { // Possibly update the icon of the tab. _UpdateTabIcon(*tab); @@ -2394,7 +2611,7 @@ namespace winrt::TerminalApp::implementation // - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the // new pane should be split from its parent. // - splitSize: the size of the split - void TerminalPage::_SplitPane(const winrt::com_ptr& tab, + void TerminalPage::_SplitPane(const winrt::com_ptr& tab, const SplitDirection splitDirection, const float splitSize, std::shared_ptr newPane) @@ -2475,10 +2692,10 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::_ToggleSplitOrientation() { - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { _UnZoomIfNeeded(); - terminalTab->ToggleSplitOrientation(); + tabImpl->ToggleSplitOrientation(); } } @@ -2492,10 +2709,10 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::_ResizePane(const ResizeDirection& direction) { - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { _UnZoomIfNeeded(); - terminalTab->ResizePane(direction); + tabImpl->ResizePane(direction); } } @@ -2507,23 +2724,23 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_ScrollPage(ScrollDirection scrollDirection) { // Do nothing if for some reason, there's no terminal tab in focus. We don't want to crash. - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { if (const auto& control{ _GetActiveControl() }) { const auto termHeight = control.ViewHeight(); auto scrollDelta = _ComputeScrollDelta(scrollDirection, termHeight); - terminalTab->Scroll(scrollDelta); + tabImpl->Scroll(scrollDelta); } } } void TerminalPage::_ScrollToBufferEdge(ScrollDirection scrollDirection) { - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { auto scrollDelta = _ComputeScrollDelta(scrollDirection, INT_MAX); - terminalTab->Scroll(scrollDelta); + tabImpl->Scroll(scrollDelta); } } @@ -2538,17 +2755,9 @@ namespace winrt::TerminalApp::implementation { if (_settings.GlobalSettings().ShowTitleInTitlebar()) { - auto selectedIndex = _tabView.SelectedIndex(); - if (selectedIndex >= 0) + if (const auto tab{ _GetFocusedTab() }) { - try - { - if (auto focusedControl{ _GetActiveControl() }) - { - return focusedControl.Title(); - } - } - CATCH_LOG(); + return tab.Title(); } } return { L"Terminal" }; @@ -2635,83 +2844,14 @@ namespace winrt::TerminalApp::implementation { if (_settings && _settings.GlobalSettings().SnapToGridOnResize()) { - if (const auto terminalTab{ _GetFocusedTabImpl() }) + if (const auto tabImpl{ _GetFocusedTabImpl() }) { - return terminalTab->CalcSnappedDimension(widthOrHeight, dimension); + return tabImpl->CalcSnappedDimension(widthOrHeight, dimension); } } return dimension; } - static wil::unique_close_clipboard_call _openClipboard(HWND hwnd) - { - bool success = false; - - // OpenClipboard may fail to acquire the internal lock --> retry. - for (DWORD sleep = 10;; sleep *= 2) - { - if (OpenClipboard(hwnd)) - { - success = true; - break; - } - // 10 iterations - if (sleep > 10000) - { - break; - } - Sleep(sleep); - } - - return wil::unique_close_clipboard_call{ success }; - } - - static winrt::hstring _extractClipboard() - { - // This handles most cases of pasting text as the OS converts most formats to CF_UNICODETEXT automatically. - if (const auto handle = GetClipboardData(CF_UNICODETEXT)) - { - const wil::unique_hglobal_locked lock{ handle }; - const auto str = static_cast(lock.get()); - if (!str) - { - return {}; - } - - const auto maxLen = GlobalSize(handle) / sizeof(wchar_t); - const auto len = wcsnlen(str, maxLen); - return winrt::hstring{ str, gsl::narrow_cast(len) }; - } - - // We get CF_HDROP when a user copied a file with Ctrl+C in Explorer and pastes that into the terminal (among others). - if (const auto handle = GetClipboardData(CF_HDROP)) - { - const wil::unique_hglobal_locked lock{ handle }; - const auto drop = static_cast(lock.get()); - if (!drop) - { - return {}; - } - - const auto cap = DragQueryFileW(drop, 0, nullptr, 0); - if (cap == 0) - { - return {}; - } - - auto buffer = winrt::impl::hstring_builder{ cap }; - const auto len = DragQueryFileW(drop, 0, buffer.data(), cap + 1); - if (len == 0) - { - return {}; - } - - return buffer.to_hstring(); - } - - return {}; - } - // Function Description: // - This function is called when the `TermControl` requests that we send // it the clipboard's content. @@ -2731,36 +2871,51 @@ namespace winrt::TerminalApp::implementation const auto weakThis = get_weak(); const auto dispatcher = Dispatcher(); const auto globalSettings = _settings.GlobalSettings(); + const auto bracketedPaste = eventArgs.BracketedPasteEnabled(); // GetClipboardData might block for up to 30s for delay-rendered contents. co_await winrt::resume_background(); winrt::hstring text; - if (const auto clipboard = _openClipboard(nullptr)) + if (const auto clipboard = clipboard::open(nullptr)) { - text = _extractClipboard(); + text = clipboard::read(); } - if (globalSettings.TrimPaste()) + if (!bracketedPaste && globalSettings.TrimPaste()) { - text = { Utils::TrimPaste(text) }; - if (text.empty()) - { - // Text is all white space, nothing to paste - co_return; - } + text = winrt::hstring{ Utils::TrimPaste(text) }; + } + + if (text.empty()) + { + co_return; + } + + bool warnMultiLine = false; + switch (globalSettings.WarnAboutMultiLinePaste()) + { + case WarnAboutMultiLinePaste::Automatic: + // NOTE that this is unsafe, because a shell that doesn't support bracketed paste + // will allow an attacker to enable the mode, not realize that, and then accept + // the paste as if it was a series of legitimate commands. See GH#13014. + warnMultiLine = !bracketedPaste; + break; + case WarnAboutMultiLinePaste::Always: + warnMultiLine = true; + break; + default: + warnMultiLine = false; + break; } - // If the requesting terminal is in bracketed paste mode, then we don't need to warn about a multi-line paste. - auto warnMultiLine = globalSettings.WarnAboutMultiLinePaste() && !eventArgs.BracketedPasteEnabled(); if (warnMultiLine) { - const auto isNewLineLambda = [](auto c) { return c == L'\n' || c == L'\r'; }; - const auto hasNewLine = std::find_if(text.cbegin(), text.cend(), isNewLineLambda) != text.cend(); - warnMultiLine = hasNewLine; + const std::wstring_view view{ text }; + warnMultiLine = view.find_first_of(L"\r\n") != std::wstring_view::npos; } - constexpr const std::size_t minimumSizeForWarning = 1024 * 5; // 5 KiB + constexpr std::size_t minimumSizeForWarning = 1024 * 5; // 5 KiB const auto warnLargeText = text.size() > minimumSizeForWarning && globalSettings.WarnAboutLargePaste(); if (warnMultiLine || warnLargeText) @@ -2770,7 +2925,7 @@ namespace winrt::TerminalApp::implementation if (const auto strongThis = weakThis.get()) { // We have to initialize the dialog here to be able to change the text of the text block within it - FindName(L"MultiLinePasteDialog").try_as(); + std::ignore = FindName(L"MultiLinePasteDialog"); ClipboardText().Text(text); // The vertical offset on the scrollbar does not reset automatically, so reset it manually @@ -2951,7 +3106,7 @@ namespace winrt::TerminalApp::implementation // - formats: dictate which formats need to be copied // Return Value: // - true iff we we able to copy text (if a selection was active) - bool TerminalPage::_CopyText(const bool dismissSelection, const bool singleLine, const bool withControlSequences, const Windows::Foundation::IReference& formats) + bool TerminalPage::_CopyText(const bool dismissSelection, const bool singleLine, const bool withControlSequences, const CopyFormat formats) { if (const auto& control{ _GetActiveControl() }) { @@ -3094,6 +3249,21 @@ namespace winrt::TerminalApp::implementation } } + void TerminalPage::_copyToClipboard(const IInspectable, const WriteToClipboardEventArgs args) const + { + if (const auto clipboard = clipboard::open(_hostingHwnd.value_or(nullptr))) + { + const auto plain = args.Plain(); + const auto html = args.Html(); + const auto rtf = args.Rtf(); + + clipboard::write( + { plain.data(), plain.size() }, + { reinterpret_cast(html.data()), html.size() }, + { reinterpret_cast(rtf.data()), rtf.size() }); + } + } + // Method Description: // - Paste text from the Windows Clipboard to the focused terminal void TerminalPage::_PasteText() @@ -3253,7 +3423,7 @@ namespace winrt::TerminalApp::implementation // connection, then we'll return nullptr. Otherwise, we'll return a new // Pane for this connection. std::shared_ptr TerminalPage::_MakeTerminalPane(const NewTerminalArgs& newTerminalArgs, - const winrt::TerminalApp::TabBase& sourceTab, + const winrt::TerminalApp::Tab& sourceTab, TerminalConnection::ITerminalConnection existingConnection) { // First things first - Check for making a pane from content ID. @@ -3271,15 +3441,15 @@ namespace winrt::TerminalApp::implementation TerminalSettingsCreateResult controlSettings{ nullptr }; Profile profile{ nullptr }; - if (const auto& terminalTab{ _GetTerminalTabImpl(sourceTab) }) + if (const auto& tabImpl{ _GetTabImpl(sourceTab) }) { - profile = terminalTab->GetFocusedProfile(); + profile = tabImpl->GetFocusedProfile(); if (profile) { // TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this. profile = GetClosestProfileForDuplicationOfProfile(profile); controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings); - const auto workingDirectory = terminalTab->GetActiveTerminalControl().WorkingDirectory(); + const auto workingDirectory = tabImpl->GetActiveTerminalControl().WorkingDirectory(); const auto validWorkingDirectory = !workingDirectory.empty(); if (validWorkingDirectory) { @@ -3343,7 +3513,7 @@ namespace winrt::TerminalApp::implementation auto debugContent{ winrt::make(profile, _terminalSettingsCache, newControl) }; auto debugPane = std::make_shared(debugContent); - // Since we're doing this split directly on the pane (instead of going through TerminalTab, + // Since we're doing this split directly on the pane (instead of going through Tab, // we need to handle the panes 'active' states // Set the pane we're splitting to active (otherwise Split will not do anything) @@ -3361,7 +3531,7 @@ namespace winrt::TerminalApp::implementation // NOTE: callers of _MakePane should be able to accept nullptr as a return // value gracefully. std::shared_ptr TerminalPage::_MakePane(const INewContentArgs& contentArgs, - const winrt::TerminalApp::TabBase& sourceTab, + const winrt::TerminalApp::Tab& sourceTab, TerminalConnection::ITerminalConnection existingConnection) { @@ -3395,9 +3565,9 @@ namespace winrt::TerminalApp::implementation // Prevent the user from opening a bunch of snippets panes. // // Look at the focused tab, and if it already has one, then just focus it. - if (const auto& focusedTab{ _GetFocusedTab() }) + if (const auto& focusedTab{ _GetFocusedTabImpl() }) { - const auto rootPane{ focusedTab.try_as()->GetRootPane() }; + const auto rootPane{ focusedTab->GetRootPane() }; const bool found = rootPane == nullptr ? false : rootPane->WalkTree([](const auto& p) -> bool { if (const auto& snippets{ p->GetContent().try_as() }) { @@ -3484,7 +3654,7 @@ namespace winrt::TerminalApp::implementation return; } - const auto path = newAppearance.ExpandedBackgroundImagePath(); + const auto path = newAppearance.BackgroundImagePath().Resolved(); if (path.empty()) { _tabContent.Background(nullptr); @@ -3553,21 +3723,21 @@ namespace winrt::TerminalApp::implementation for (const auto& tab : _tabs) { - if (auto terminalTab{ _GetTerminalTabImpl(tab) }) + if (auto tabImpl{ _GetTabImpl(tab) }) { // Let the tab know that there are new settings. It's up to each content to decide what to do with them. - terminalTab->UpdateSettings(_settings); + tabImpl->UpdateSettings(_settings); // Update the icon of the tab for the currently focused profile in that tab. // Only do this for TerminalTabs. Other types of tabs won't have multiple panes // and profiles so the Title and Icon will be set once and only once on init. - _UpdateTabIcon(*terminalTab); + _UpdateTabIcon(*tabImpl); // Force the TerminalTab to re-grab its currently active control's title. - terminalTab->UpdateTitle(); + tabImpl->UpdateTitle(); } - auto tabImpl{ winrt::get_self(tab) }; + auto tabImpl{ winrt::get_self(tab) }; tabImpl->SetActionMap(_settings.ActionMap()); } @@ -3607,6 +3777,9 @@ namespace winrt::TerminalApp::implementation _updateThemeColors(); _updateAllTabCloseButtons(); + + // The user may have changed the "show title in titlebar" setting. + TitleChanged.raise(*this, nullptr); } void TerminalPage::_updateAllTabCloseButtons() @@ -3653,25 +3826,9 @@ namespace winrt::TerminalApp::implementation _startupActions = std::move(actions); } - // Routine Description: - // - Notifies this Terminal Page that it should start the incoming connection - // listener for command-line tools attempting to join this Terminal - // through the default application channel. - // Arguments: - // - isEmbedding - True if COM started us to be a server. False if we're doing it of our own accord. - // Return Value: - // - - void TerminalPage::SetInboundListener(bool isEmbedding) + void TerminalPage::SetStartupConnection(ITerminalConnection connection) { - _shouldStartInboundListener = true; - _isEmbeddingInboundListener = isEmbedding; - - // If the page has already passed the NotInitialized state, - // then it is ready-enough for us to just start this immediately. - if (_startupState != StartupState::NotInitialized) - { - _StartInboundListener(); - } + _startupConnection = std::move(connection); } winrt::TerminalApp::IDialogPresenter TerminalPage::DialogPresenter() const @@ -3702,7 +3859,7 @@ namespace winrt::TerminalApp::implementation for (const auto& tab : _tabs) { - if (auto tabImpl{ _GetTerminalTabImpl(tab) }) + if (auto tabImpl{ _GetTabImpl(tab) }) { auto tabState{ tabImpl->GetCombinedTaskbarState() }; // lowest priority wins @@ -3745,11 +3902,11 @@ namespace winrt::TerminalApp::implementation _visible = showOrHide; for (const auto& tab : _tabs) { - if (auto terminalTab{ _GetTerminalTabImpl(tab) }) + if (auto tabImpl{ _GetTabImpl(tab) }) { // Manually enumerate the panes in each tab; this will let us recycle TerminalSettings // objects but only have to iterate one time. - terminalTab->GetRootPane()->WalkTree([&](auto&& pane) { + tabImpl->GetRootPane()->WalkTree([&](auto&& pane) { if (auto control = pane->GetTerminalControl()) { control.WindowVisibilityChanged(showOrHide); @@ -3767,7 +3924,7 @@ namespace winrt::TerminalApp::implementation // - tab: the tab where the search box should be created // Return Value: // - - void TerminalPage::_Find(const TerminalTab& tab) + void TerminalPage::_Find(const Tab& tab) { if (const auto& control{ tab.GetActiveTerminalControl() }) { @@ -3833,36 +3990,18 @@ namespace winrt::TerminalApp::implementation // and the non-client are behind it // Return Value: // - - void TerminalPage::_SetNewTabButtonColor(const Windows::UI::Color& color, const Windows::UI::Color& accentColor) + void TerminalPage::_SetNewTabButtonColor(const til::color color, const til::color accentColor) { + constexpr auto lightnessThreshold = 0.6f; // TODO GH#3327: Look at what to do with the tab button when we have XAML theming - auto IsBrightColor = ColorHelper::IsBrightColor(color); - auto isLightAccentColor = ColorHelper::IsBrightColor(accentColor); - winrt::Windows::UI::Color pressedColor{}; - winrt::Windows::UI::Color hoverColor{}; - winrt::Windows::UI::Color foregroundColor{}; - const auto hoverColorAdjustment = 5.f; - const auto pressedColorAdjustment = 7.f; + const auto IsBrightColor = ColorFix::GetLightness(color) >= lightnessThreshold; + const auto isLightAccentColor = ColorFix::GetLightness(accentColor) >= lightnessThreshold; + const auto hoverColorAdjustment = isLightAccentColor ? -0.05f : 0.05f; + const auto pressedColorAdjustment = isLightAccentColor ? -0.1f : 0.1f; - if (IsBrightColor) - { - foregroundColor = winrt::Windows::UI::Colors::Black(); - } - else - { - foregroundColor = winrt::Windows::UI::Colors::White(); - } - - if (isLightAccentColor) - { - hoverColor = ColorHelper::Darken(accentColor, hoverColorAdjustment); - pressedColor = ColorHelper::Darken(accentColor, pressedColorAdjustment); - } - else - { - hoverColor = ColorHelper::Lighten(accentColor, hoverColorAdjustment); - pressedColor = ColorHelper::Lighten(accentColor, pressedColorAdjustment); - } + const auto foregroundColor = IsBrightColor ? Colors::Black() : Colors::White(); + const auto hoverColor = til::color{ ColorFix::AdjustLightness(accentColor, hoverColorAdjustment) }; + const auto pressedColor = til::color{ ColorFix::AdjustLightness(accentColor, pressedColorAdjustment) }; Media::SolidColorBrush backgroundBrush{ accentColor }; Media::SolidColorBrush backgroundHoverBrush{ hoverColor }; @@ -3885,7 +4024,7 @@ namespace winrt::TerminalApp::implementation _newTabButton.Background(backgroundBrush); _newTabButton.Foreground(foregroundBrush); - // This is just like what we do in TabBase::_RefreshVisualState. We need + // This is just like what we do in Tab::_RefreshVisualState. We need // to manually toggle the visual state, so the setters in the visual // state group will re-apply, and set our currently selected colors in // the resources. @@ -3970,7 +4109,7 @@ namespace winrt::TerminalApp::implementation // Arguments: // - args: the ExecuteCommandlineArgs to synthesize a list of startup actions for. // Return Value: - // - an empty list if we failed to parse, otherwise a list of actions to execute. + // - an empty list if we failed to parse; otherwise, a list of actions to execute. std::vector TerminalPage::ConvertExecuteCommandlineToActions(const ExecuteCommandlineArgs& args) { ::TerminalApp::AppCommandlineArgs appArgs; @@ -4073,68 +4212,6 @@ namespace winrt::TerminalApp::implementation ChangeMaximizeRequested.raise(*this, nullptr); } - HRESULT TerminalPage::_OnNewConnection(const ConptyConnection& connection) - { - _newConnectionRevoker.revoke(); - - // We need to be on the UI thread in order for _OpenNewTab to run successfully. - // HasThreadAccess will return true if we're currently on a UI thread and false otherwise. - // When we're on a COM thread, we'll need to dispatch the calls to the UI thread - // and wait on it hence the locking mechanism. - if (!Dispatcher().HasThreadAccess()) - { - til::latch latch{ 1 }; - auto finalVal = S_OK; - - Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() { - finalVal = _OnNewConnection(connection); - latch.count_down(); - }); - - latch.wait(); - return finalVal; - } - - try - { - NewTerminalArgs newTerminalArgs; - newTerminalArgs.Commandline(connection.Commandline()); - newTerminalArgs.TabTitle(connection.StartingTitle()); - // GH #12370: We absolutely cannot allow a defterm connection to - // auto-elevate. Defterm doesn't work for elevated scenarios in the - // first place. If we try accepting the connection, the spawning an - // elevated version of the Terminal with that profile... that's a - // recipe for disaster. We won't ever open up a tab in this window. - newTerminalArgs.Elevate(false); - const auto newPane = _MakePane(newTerminalArgs, nullptr, connection); - newPane->WalkTree([](const auto& pane) { - pane->FinalizeConfigurationGivenDefault(); - }); - _CreateNewTabFromPane(newPane); - - // Request a summon of this window to the foreground - SummonWindowRequested.raise(*this, nullptr); - - // TEMPORARY SOLUTION - // If the connection has requested for the window to be maximized, - // manually maximize it here. Ideally, we should be _initializing_ - // the session maximized, instead of manually maximizing it after initialization. - // However, because of the current way our defterm handoff works, - // we are unable to get the connection info before the terminal session - // has already started. - - // Make sure that there were no other tabs already existing (in - // the case that we are in glomming mode), because we don't want - // to be maximizing other existing sessions that did not ask for it. - if (_tabs.Size() == 1 && connection.ShowWindow() == SW_SHOWMAXIMIZED) - { - RequestSetMaximized(true); - } - return S_OK; - } - CATCH_RETURN() - } - TerminalApp::IPaneContent TerminalPage::_makeSettingsContent() { if (auto app{ winrt::Windows::UI::Xaml::Application::Current().try_as() }) @@ -4165,6 +4242,13 @@ namespace winrt::TerminalApp::implementation } }); + sui.ShowLoadWarningsDialog([weakThis{ get_weak() }](auto&& /*s*/, const Windows::Foundation::Collections::IVectorView& warnings) { + if (auto page{ weakThis.get() }) + { + page->ShowLoadWarningsDialog.raise(*page, warnings); + } + }); + return *settingsContent; } @@ -4191,25 +4275,18 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Returns a com_ptr to the implementation type of the given tab if it's a TerminalTab. + // - Returns a com_ptr to the implementation type of the given tab if it's a Tab. // If the tab is not a TerminalTab, returns nullptr. // Arguments: // - tab: the projected type of a Tab // Return Value: // - If the tab is a TerminalTab, a com_ptr to the implementation type. // If the tab is not a TerminalTab, nullptr - winrt::com_ptr TerminalPage::_GetTerminalTabImpl(const TerminalApp::TabBase& tab) + winrt::com_ptr TerminalPage::_GetTabImpl(const TerminalApp::Tab& tab) { - if (auto terminalTab = tab.try_as()) - { - winrt::com_ptr tabImpl; - tabImpl.copy_from(winrt::get_self(terminalTab)); - return tabImpl; - } - else - { - return nullptr; - } + winrt::com_ptr tabImpl; + tabImpl.copy_from(winrt::get_self(tab)); + return tabImpl; } // Method Description: @@ -4696,10 +4773,10 @@ namespace winrt::TerminalApp::implementation for (const auto& tab : _tabs) { - if (auto terminalTab{ _GetTerminalTabImpl(tab) }) + if (auto tabImpl{ _GetTabImpl(tab) }) { // The root pane will propagate the theme change to all its children. - if (const auto& rootPane{ terminalTab->GetRootPane() }) + if (const auto& rootPane{ tabImpl->GetRootPane() }) { rootPane->UpdateResources(_paneResources); } @@ -4760,7 +4837,7 @@ namespace winrt::TerminalApp::implementation } // Second: Update the colors of our individual TabViewItems. This - // applies tab.background to the tabs via TerminalTab::ThemeColor. + // applies tab.background to the tabs via Tab::ThemeColor. // // Do this second, so that we already know the bgColor of the titlebar. { @@ -4768,8 +4845,8 @@ namespace winrt::TerminalApp::implementation const auto tabUnfocusedBackground = theme.Tab() ? theme.Tab().UnfocusedBackground() : nullptr; for (const auto& tab : _tabs) { - winrt::com_ptr tabImpl; - tabImpl.copy_from(winrt::get_self(tab)); + winrt::com_ptr tabImpl; + tabImpl.copy_from(winrt::get_self(tab)); tabImpl->ThemeColor(tabBackground, tabUnfocusedBackground, bgColor); } } @@ -4885,8 +4962,6 @@ namespace winrt::TerminalApp::implementation safe_void_coroutine TerminalPage::_ControlCompletionsChangedHandler(const IInspectable sender, const CompletionsChangedEventArgs args) { - // This will come in on a background (not-UI, not output) thread. - // This won't even get hit if the velocity flag is disabled - we gate // registering for the event based off of // Feature_ShellCompletions::IsEnabled back in _RegisterTerminalEvents @@ -5060,7 +5135,7 @@ namespace winrt::TerminalApp::implementation makeItem(RS_(L"DuplicateTabText"), L"\xF5ED", ActionAndArgs{ ShortcutAction::DuplicateTab, nullptr }, menu); const auto focusedProfileName = focusedProfile.Name(); - const auto focusedProfileIcon = focusedProfile.Icon(); + const auto focusedProfileIcon = focusedProfile.Icon().Resolved(); const auto splitPaneDuplicateText = RS_(L"SplitPaneDuplicateText") + L" " + focusedProfileName; // SplitPaneDuplicateText const auto splitPaneRightText = RS_(L"SplitPaneRightText"); @@ -5086,7 +5161,7 @@ namespace winrt::TerminalApp::implementation { const auto profile = activeProfiles.GetAt(profileIndex); const auto profileName = profile.Name(); - const auto profileIcon = profile.Icon(); + const auto profileIcon = profile.Icon().Resolved(); NewTerminalArgs args{}; args.Profile(profileName); @@ -5121,22 +5196,22 @@ namespace winrt::TerminalApp::implementation if (auto neighbor = rootPane->NavigateDirection(activePane, FocusDirection::Down, mruPanes)) { - makeItem(RS_(L"SwapPaneDownText"), neighbor->GetProfile().Icon(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Down } }, swapPaneMenu); + makeItem(RS_(L"SwapPaneDownText"), neighbor->GetProfile().Icon().Resolved(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Down } }, swapPaneMenu); } if (auto neighbor = rootPane->NavigateDirection(activePane, FocusDirection::Right, mruPanes)) { - makeItem(RS_(L"SwapPaneRightText"), neighbor->GetProfile().Icon(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Right } }, swapPaneMenu); + makeItem(RS_(L"SwapPaneRightText"), neighbor->GetProfile().Icon().Resolved(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Right } }, swapPaneMenu); } if (auto neighbor = rootPane->NavigateDirection(activePane, FocusDirection::Up, mruPanes)) { - makeItem(RS_(L"SwapPaneUpText"), neighbor->GetProfile().Icon(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Up } }, swapPaneMenu); + makeItem(RS_(L"SwapPaneUpText"), neighbor->GetProfile().Icon().Resolved(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Up } }, swapPaneMenu); } if (auto neighbor = rootPane->NavigateDirection(activePane, FocusDirection::Left, mruPanes)) { - makeItem(RS_(L"SwapPaneLeftText"), neighbor->GetProfile().Icon(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Left } }, swapPaneMenu); + makeItem(RS_(L"SwapPaneLeftText"), neighbor->GetProfile().Icon().Resolved(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Left } }, swapPaneMenu); } makeMenuItem(RS_(L"SwapPaneText"), L"\xF1CB", swapPaneMenu, menu); @@ -5238,8 +5313,8 @@ namespace winrt::TerminalApp::implementation // Get the tab impl from this event. const auto eventTab = e.Tab(); const auto tabBase = _GetTabByTabViewItem(eventTab); - winrt::com_ptr tabImpl; - tabImpl.copy_from(winrt::get_self(tabBase)); + winrt::com_ptr tabImpl; + tabImpl.copy_from(winrt::get_self(tabBase)); if (tabImpl) { // First: stash the tab we started dragging. @@ -5450,6 +5525,14 @@ namespace winrt::TerminalApp::implementation runAsAdminItem.Click([profileIndex, weakThis{ get_weak() }](auto&&, auto&&) { if (auto page{ weakThis.get() }) { + TraceLoggingWrite( + g_hTerminalAppProvider, + "NewTabMenuItemElevateSubmenuItemClicked", + TraceLoggingDescription("Event emitted when the elevate submenu item from the new tab menu is invoked"), + TraceLoggingValue(page->NumberOfTabs(), "TabCount", "The count of tabs currently opened in this window"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + NewTerminalArgs args{ profileIndex }; args.Elevate(true); page->_OpenNewTerminalViaDropdown(args); diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index b369bd920e..36ecec4fd2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -4,7 +4,7 @@ #pragma once #include "TerminalPage.g.h" -#include "TerminalTab.h" +#include "Tab.h" #include "AppKeyBindings.h" #include "AppCommandlineArgs.h" #include "RenameWindowRequestedArgs.g.h" @@ -129,8 +129,8 @@ namespace winrt::TerminalApp::implementation void RequestSetMaximized(bool newMaximized); void SetStartupActions(std::vector actions); + void SetStartupConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); - void SetInboundListener(bool isEmbedding); static std::vector ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args); winrt::TerminalApp::IDialogPresenter DialogPresenter() const; @@ -147,9 +147,9 @@ namespace winrt::TerminalApp::implementation void ShowTerminalWorkingDirectory(); safe_void_coroutine ProcessStartupActions(std::vector actions, - const bool initial, const winrt::hstring cwd = winrt::hstring{}, const winrt::hstring env = winrt::hstring{}); + safe_void_coroutine CreateTabFromConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); TerminalApp::WindowProperties WindowProperties() const noexcept { return _WindowProperties; }; @@ -169,7 +169,7 @@ namespace winrt::TerminalApp::implementation til::property_changed_event PropertyChanged; // -------------------------------- WinRT Events --------------------------------- - til::typed_event TitleChanged; + til::typed_event TitleChanged; til::typed_event CloseWindowRequested; til::typed_event SetTitleBarContent; til::typed_event FocusModeChanged; @@ -187,6 +187,7 @@ namespace winrt::TerminalApp::implementation til::typed_event OpenSystemMenu; til::typed_event QuitRequested; til::typed_event ShowWindowChanged; + til::typed_event> ShowLoadWarningsDialog; til::typed_event RequestMoveContent; til::typed_event RequestReceiveContent; @@ -218,13 +219,13 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; - Windows::Foundation::Collections::IObservableVector _tabs; - Windows::Foundation::Collections::IObservableVector _mruTabs; - static winrt::com_ptr _GetTerminalTabImpl(const TerminalApp::TabBase& tab); + Windows::Foundation::Collections::IObservableVector _tabs; + Windows::Foundation::Collections::IObservableVector _mruTabs; + static winrt::com_ptr _GetTabImpl(const TerminalApp::Tab& tab); void _UpdateTabIndices(); - TerminalApp::TerminalTab _settingsTab{ nullptr }; + TerminalApp::Tab _settingsTab{ nullptr }; bool _isInFocusMode{ false }; bool _isFullscreen{ false }; @@ -256,8 +257,7 @@ namespace winrt::TerminalApp::implementation StartupState _startupState{ StartupState::NotInitialized }; std::vector _startupActions; - bool _shouldStartInboundListener{ false }; - bool _isEmbeddingInboundListener{ false }; + winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _startupConnection{ nullptr }; std::shared_ptr _windowIdToast{ nullptr }; std::shared_ptr _actionSavedToast{ nullptr }; @@ -277,12 +277,10 @@ namespace winrt::TerminalApp::implementation struct StashedDragData { - winrt::com_ptr draggedTab{ nullptr }; + winrt::com_ptr draggedTab{ nullptr }; winrt::Windows::Foundation::Point dragOffset{ 0, 0 }; } _stashed; - winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::NewConnection_revoker _newConnectionRevoker; - safe_void_coroutine _NewTerminalByDrop(const Windows::Foundation::IInspectable&, winrt::Windows::UI::Xaml::DragEventArgs e); __declspec(noinline) CommandPalette _loadCommandPaletteSlowPath(); @@ -307,7 +305,7 @@ namespace winrt::TerminalApp::implementation void _OpenNewTabDropdown(); HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::INewContentArgs& newContentArgs); - TerminalApp::TerminalTab _CreateNewTabFromPane(std::shared_ptr pane, uint32_t insertPosition = -1); + TerminalApp::Tab _CreateNewTabFromPane(std::shared_ptr pane, uint32_t insertPosition = -1); std::wstring _evaluatePathForCwd(std::wstring_view path); @@ -330,25 +328,25 @@ namespace winrt::TerminalApp::implementation void _HookupKeyBindings(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap) noexcept; void _RegisterActionCallbacks(); - void _UpdateTitle(const TerminalTab& tab); - void _UpdateTabIcon(TerminalTab& tab); + void _UpdateTitle(const Tab& tab); + void _UpdateTabIcon(Tab& tab); void _UpdateTabView(); void _UpdateTabWidthMode(); void _SetBackgroundImage(const winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig& newAppearance); void _DuplicateFocusedTab(); - void _DuplicateTab(const TerminalTab& tab); + void _DuplicateTab(const Tab& tab); - safe_void_coroutine _ExportTab(const TerminalTab& tab, winrt::hstring filepath); + safe_void_coroutine _ExportTab(const Tab& tab, winrt::hstring filepath); - winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::TabBase tab); + winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::Tab tab); void _CloseTabAtIndex(uint32_t index); - void _RemoveTab(const winrt::TerminalApp::TabBase& tab); - safe_void_coroutine _RemoveTabs(const std::vector tabs); + void _RemoveTab(const winrt::TerminalApp::Tab& tab); + safe_void_coroutine _RemoveTabs(const std::vector tabs); - void _InitializeTab(winrt::com_ptr newTabImpl, uint32_t insertPosition = -1); + void _InitializeTab(winrt::com_ptr newTabImpl, uint32_t insertPosition = -1); void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term); - void _RegisterTabEvents(TerminalTab& hostingTab); + void _RegisterTabEvents(Tab& hostingTab); void _DismissTabContextMenus(); void _FocusCurrentTab(const bool focusAlways); @@ -359,7 +357,7 @@ namespace winrt::TerminalApp::implementation bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction); bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction); bool _MovePane(const Microsoft::Terminal::Settings::Model::MovePaneArgs args); - bool _MoveTab(winrt::com_ptr tab, const Microsoft::Terminal::Settings::Model::MoveTabArgs args); + bool _MoveTab(winrt::com_ptr tab, const Microsoft::Terminal::Settings::Model::MoveTabArgs args); template bool _ApplyToActiveControls(F f) @@ -383,21 +381,21 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl(); std::optional _GetFocusedTabIndex() const noexcept; - std::optional _GetTabIndex(const TerminalApp::TabBase& tab) const noexcept; - TerminalApp::TabBase _GetFocusedTab() const noexcept; - winrt::com_ptr _GetFocusedTabImpl() const noexcept; - TerminalApp::TabBase _GetTabByTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem) const noexcept; + std::optional _GetTabIndex(const TerminalApp::Tab& tab) const noexcept; + TerminalApp::Tab _GetFocusedTab() const noexcept; + winrt::com_ptr _GetFocusedTabImpl() const noexcept; + TerminalApp::Tab _GetTabByTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem) const noexcept; void _HandleClosePaneRequested(std::shared_ptr pane); - safe_void_coroutine _SetFocusedTab(const winrt::TerminalApp::TabBase tab); + safe_void_coroutine _SetFocusedTab(const winrt::TerminalApp::Tab tab); safe_void_coroutine _CloseFocusedPane(); - void _ClosePanes(weak_ref weakTab, std::vector paneIds); + void _ClosePanes(weak_ref weakTab, std::vector paneIds); winrt::Windows::Foundation::IAsyncOperation _PaneConfirmCloseReadOnly(std::shared_ptr pane); void _AddPreviouslyClosedPaneOrTab(std::vector&& args); void _Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference& rowsToScroll); - void _SplitPane(const winrt::com_ptr& tab, + void _SplitPane(const winrt::com_ptr& tab, const Microsoft::Terminal::Settings::Model::SplitDirection splitType, const float splitSize, std::shared_ptr newPane); @@ -415,10 +413,11 @@ namespace winrt::TerminalApp::implementation bool _IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri); void _ShowCouldNotOpenDialog(winrt::hstring reason, winrt::hstring uri); - bool _CopyText(const bool dismissSelection, const bool singleLine, const bool withControlSequences, const Windows::Foundation::IReference& formats); + bool _CopyText(bool dismissSelection, bool singleLine, bool withControlSequences, Microsoft::Terminal::Control::CopyFormat formats); safe_void_coroutine _SetTaskbarProgressHandler(const IInspectable sender, const IInspectable eventArgs); + void _copyToClipboard(IInspectable, Microsoft::Terminal::Control::WriteToClipboardEventArgs args) const; void _PasteText(); safe_void_coroutine _ControlNoticeRaisedHandler(const IInspectable sender, const Microsoft::Terminal::Control::NoticeEventArgs eventArgs); @@ -441,14 +440,14 @@ namespace winrt::TerminalApp::implementation void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs); void _OnTabCloseRequested(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabCloseRequestedEventArgs& eventArgs); void _OnFirstLayout(const IInspectable& sender, const IInspectable& eventArgs); - void _UpdatedSelectedTab(const winrt::TerminalApp::TabBase& tab); + void _UpdatedSelectedTab(const winrt::TerminalApp::Tab& tab); void _UpdateBackground(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); void _OnDispatchCommandRequested(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::Command& command); void _OnCommandLineExecutionRequested(const IInspectable& sender, const winrt::hstring& commandLine); - void _OnSwitchToTabRequested(const IInspectable& sender, const winrt::TerminalApp::TabBase& tab); + void _OnSwitchToTabRequested(const IInspectable& sender, const winrt::TerminalApp::Tab& tab); - void _Find(const TerminalTab& tab); + void _Find(const Tab& tab); winrt::Microsoft::Terminal::Control::TermControl _CreateNewControlAndContent(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection); @@ -457,19 +456,17 @@ namespace winrt::TerminalApp::implementation TerminalApp::IPaneContent _makeSettingsContent(); std::shared_ptr _MakeTerminalPane(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr, - const winrt::TerminalApp::TabBase& sourceTab = nullptr, + const winrt::TerminalApp::Tab& sourceTab = nullptr, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); std::shared_ptr _MakePane(const Microsoft::Terminal::Settings::Model::INewContentArgs& newContentArgs = nullptr, - const winrt::TerminalApp::TabBase& sourceTab = nullptr, + const winrt::TerminalApp::Tab& sourceTab = nullptr, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); void _RefreshUIForSettingsReload(); - void _SetNewTabButtonColor(const Windows::UI::Color& color, const Windows::UI::Color& accentColor); + void _SetNewTabButtonColor(til::color color, til::color accentColor); void _ClearNewTabButtonColor(); - void _StartInboundListener(); - safe_void_coroutine _CompleteInitialization(); void _FocusActiveControl(IInspectable sender, IInspectable eventArgs); @@ -479,7 +476,7 @@ namespace winrt::TerminalApp::implementation static int _ComputeScrollDelta(ScrollDirection scrollDirection, const uint32_t rowsToScroll); static uint32_t _ReadSystemRowsToScroll(); - void _UpdateMRUTab(const winrt::TerminalApp::TabBase& tab); + void _UpdateMRUTab(const winrt::TerminalApp::Tab& tab); void _TryMoveTab(const uint32_t currentTabIndex, const int32_t suggestedNewTabIndex); @@ -538,7 +535,7 @@ namespace winrt::TerminalApp::implementation void _onTabDroppedOutside(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e); void _DetachPaneFromWindow(std::shared_ptr pane); - void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); + void _DetachTabFromWindow(const winrt::com_ptr& tabImpl); void _MoveContent(std::vector&& actions, const winrt::hstring& windowName, const uint32_t tabIndex, @@ -550,9 +547,9 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Controls::MenuFlyout _CreateRunAsAdminFlyout(int profileIndex); winrt::Microsoft::Terminal::Control::TermControl _senderOrActiveControl(const winrt::Windows::Foundation::IInspectable& sender); - winrt::com_ptr _senderOrFocusedTab(const IInspectable& sender); + winrt::com_ptr _senderOrFocusedTab(const IInspectable& sender); - void _activePaneChanged(winrt::TerminalApp::TerminalTab tab, Windows::Foundation::IInspectable args); + void _activePaneChanged(winrt::TerminalApp::Tab tab, Windows::Foundation::IInspectable args); safe_void_coroutine _doHandleSuggestions(Microsoft::Terminal::Settings::Model::SuggestionsArgs realArgs); #pragma region ActionHandlers diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 2788cedd10..ce15059864 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -82,7 +82,7 @@ namespace TerminalApp void WindowActivated(Boolean activated); void SendContentToOther(RequestReceiveContentArgs args); - event Windows.Foundation.TypedEventHandler TitleChanged; + event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler CloseWindowRequested; event Windows.Foundation.TypedEventHandler SetTitleBarContent; event Windows.Foundation.TypedEventHandler FocusModeChanged; @@ -97,6 +97,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler OpenSystemMenu; event Windows.Foundation.TypedEventHandler ShowWindowChanged; + event Windows.Foundation.TypedEventHandler > ShowLoadWarningsDialog; event Windows.Foundation.TypedEventHandler RequestMoveContent; event Windows.Foundation.TypedEventHandler RequestReceiveContent; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index c85009ea0c..002e4f026c 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -82,7 +82,7 @@ namespace winrt::TerminalApp::implementation winrt::hstring TerminalPaneContent::Icon() const { - return _profile.EvaluatedIcon(); + return _profile.Icon().Resolved(); } Windows::Foundation::IReference TerminalPaneContent::TabColor() const noexcept @@ -95,7 +95,7 @@ namespace winrt::TerminalApp::implementation NewTerminalArgs args{}; const auto& controlSettings = _control.Settings(); - args.Profile(controlSettings.ProfileName()); + args.Profile(::Microsoft::Console::Utils::GuidToString(_profile.Guid())); // If we know the user's working directory use it instead of the profile. if (const auto dir = _control.WorkingDirectory(); !dir.empty()) { @@ -340,8 +340,12 @@ namespace winrt::TerminalApp::implementation RestartTerminalRequested.raise(*this, nullptr); } - void TerminalPaneContent::UpdateSettings(const CascadiaSettings& /*settings*/) + void TerminalPaneContent::UpdateSettings(const CascadiaSettings& settings) { + // Reload our profile from the settings model to propagate bell mode, icon, and close on exit mode (anything that uses _profile). + const auto profile{ settings.FindProfile(_profile.Guid()) }; + _profile = profile ? profile : settings.ProfileDefaults(); + if (const auto& settings{ _cache.TryLookup(_profile) }) { _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings()); diff --git a/src/cascadia/TerminalApp/TerminalTab.idl b/src/cascadia/TerminalApp/TerminalTab.idl deleted file mode 100644 index 5dbd845d49..0000000000 --- a/src/cascadia/TerminalApp/TerminalTab.idl +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import "TabBase.idl"; -import "TerminalTabStatus.idl"; - -namespace TerminalApp -{ - [default_interface] runtimeclass TerminalTab : TabBase - { - TerminalTabStatus TabStatus { get; }; - } -} diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 65518b5fdc..a2166c0169 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -40,8 +40,7 @@ static const std::array settingsLoadWarningsLabels{ USES_RESOURCE(L"MissingDefaultProfileText"), USES_RESOURCE(L"DuplicateProfileText"), USES_RESOURCE(L"UnknownColorSchemeText"), - USES_RESOURCE(L"InvalidBackgroundImage"), - USES_RESOURCE(L"InvalidIcon"), + USES_RESOURCE(L"InvalidMediaResource"), USES_RESOURCE(L"AtLeastOneKeybindingWarning"), USES_RESOURCE(L"TooManyKeysForChord"), USES_RESOURCE(L"MissingRequiredParameter"), @@ -152,38 +151,23 @@ namespace winrt::TerminalApp::implementation // instead. // * if we have commandline arguments, Pass commandline args into the // TerminalPage. - if (!_initialContentArgs.empty()) + if (_startupConnection) { - _root->SetStartupActions(_initialContentArgs); + _root->SetStartupConnection(std::move(_startupConnection)); } - else + else if (!_initialContentArgs.empty()) + { + _root->SetStartupActions(std::move(_initialContentArgs)); + } + else if (const auto& layout = LoadPersistedLayout()) { // layout will only ever be non-null if there were >0 tabs persisted in // .TabLayout(). We can re-evaluate that as a part of TODO: GH#12633 - if (const auto& layout = LoadPersistedLayout()) - { - std::vector actions; - for (const auto& a : layout.TabLayout()) - { - actions.emplace_back(a); - } - _root->SetStartupActions(actions); - } - else if (_appArgs) - { - _root->SetStartupActions(_appArgs->ParsedArgs().GetStartupActions()); - } + _root->SetStartupActions(wil::to_vector(layout.TabLayout())); } - - // Check if we were started as a COM server for inbound connections of console sessions - // coming out of the operating system default application feature. If so, - // tell TerminalPage to start the listener as we have to make sure it has the chance - // to register a handler to hear about the requests first and is all ready to receive - // them before the COM server registers itself. Otherwise, the request might come - // in and be routed to an event with no handlers or a non-ready Page. - if (_appArgs && _appArgs->ParsedArgs().IsHandoffListener()) + else if (_appArgs) { - _root->SetInboundListener(true); + _root->SetStartupActions(_appArgs->ParsedArgs().GetStartupActions()); } return _root->Initialize(hwnd); @@ -220,6 +204,7 @@ namespace winrt::TerminalApp::implementation _root->Initialized({ get_weak(), &TerminalWindow::_pageInitialized }); _root->WindowSizeChanged({ get_weak(), &TerminalWindow::_WindowSizeChanged }); _root->RenameWindowRequested({ get_weak(), &TerminalWindow::_RenameWindowRequested }); + _root->ShowLoadWarningsDialog({ get_weak(), &TerminalWindow::_ShowLoadWarningsDialog }); _root->Create(); AppLogic::Current()->SettingsChanged({ get_weak(), &TerminalWindow::UpdateSettingsHandler }); @@ -326,7 +311,7 @@ namespace winrt::TerminalApp::implementation // - Show a ContentDialog with buttons to take further action. Uses the // FrameworkElements provided as the title and content of this dialog, and // displays buttons (or a single button). Two buttons (primary and secondary) - // will be displayed if this is an warning dialog for closing the terminal, + // will be displayed if this is a warning dialog for closing the terminal, // this allows the users to abandon the closing action. Otherwise, a single // close button will be displayed. // - Only one dialog can be visible at a time. If another dialog is visible @@ -478,7 +463,7 @@ namespace winrt::TerminalApp::implementation // validating the settings. // - Only one dialog can be visible at a time. If another dialog is visible // when this is called, nothing happens. See ShowDialog for details - void TerminalWindow::_ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector& warnings) + void TerminalWindow::_ShowLoadWarningsDialog(const IInspectable&, const Windows::Foundation::Collections::IVectorView& warnings) { auto title = RS_(L"SettingsValidateErrorTitle"); auto buttonText = RS_(L"Ok"); @@ -539,7 +524,7 @@ namespace winrt::TerminalApp::implementation } else if (settingsLoadedResult == S_FALSE) { - _ShowLoadWarningsDialog(_initialLoadResult.Warnings()); + _ShowLoadWarningsDialog(nullptr, _initialLoadResult.Warnings()); } } @@ -825,7 +810,7 @@ namespace winrt::TerminalApp::implementation } else if (args.Result() == S_FALSE) { - _ShowLoadWarningsDialog(args.Warnings()); + _ShowLoadWarningsDialog(nullptr, args.Warnings()); } else if (args.Result() == S_OK) { @@ -1053,6 +1038,7 @@ namespace winrt::TerminalApp::implementation int32_t TerminalWindow::SetStartupCommandline(TerminalApp::CommandlineArgs args) { _appArgs = winrt::get_self(args); + _startupConnection = args.Connection(); auto& parsedArgs = _appArgs->ParsedArgs(); _WindowProperties->SetInitialCwd(_appArgs->CurrentDirectory()); @@ -1113,13 +1099,16 @@ namespace winrt::TerminalApp::implementation auto& parsedArgs = _appArgs->ParsedArgs(); auto& actions = parsedArgs.GetStartupActions(); - _root->ProcessStartupActions(actions, false, _appArgs->CurrentDirectory(), _appArgs->CurrentEnvironment()); - - if (parsedArgs.IsHandoffListener()) + if (auto conn = args.Connection()) { - _root->SetInboundListener(true); + _root->CreateTabFromConnection(std::move(conn)); + } + else if (!actions.empty()) + { + _root->ProcessStartupActions(actions, _appArgs->CurrentDirectory(), _appArgs->CurrentEnvironment()); } } + // Return the result of parsing with commandline, though it may or may not be used. return _appArgs->ExitCode(); } diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index fd27617846..68f4c940a3 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -169,6 +169,7 @@ namespace winrt::TerminalApp::implementation winrt::com_ptr _root{ nullptr }; wil::com_ptr _appArgs{ nullptr }; + winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _startupConnection{ nullptr }; bool _hasCommandLineArguments{ false }; bool _gotSettingsStartupActions{ false }; std::vector _settingsStartupArgs{}; @@ -189,7 +190,7 @@ namespace winrt::TerminalApp::implementation const winrt::hstring& contentKey, HRESULT settingsLoadedResult, const winrt::hstring& exceptionText); - void _ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector& warnings); + void _ShowLoadWarningsDialog(const IInspectable& sender, const Windows::Foundation::Collections::IVectorView& warnings); bool _IsKeyboardServiceEnabled(); @@ -209,7 +210,7 @@ namespace winrt::TerminalApp::implementation FORWARDED_TYPED_EVENT(Initialized, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, Initialized); FORWARDED_TYPED_EVENT(SetTitleBarContent, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement, _root, SetTitleBarContent); - FORWARDED_TYPED_EVENT(TitleChanged, winrt::Windows::Foundation::IInspectable, winrt::hstring, _root, TitleChanged); + FORWARDED_TYPED_EVENT(TitleChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, TitleChanged); FORWARDED_TYPED_EVENT(CloseWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, CloseWindowRequested); FORWARDED_TYPED_EVENT(FocusModeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FocusModeChanged); FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index baf58317df..6457a74061 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -30,7 +30,7 @@ namespace TerminalApp { Boolean Reload { get; }; UInt64 Result { get; }; - IVector Warnings { get; }; + IVectorView Warnings { get; }; String ExceptionText { get; }; Microsoft.Terminal.Settings.Model.CascadiaSettings NewSettings { get; }; @@ -112,7 +112,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler Initialized; event Windows.Foundation.TypedEventHandler SetTitleBarContent; - event Windows.Foundation.TypedEventHandler TitleChanged; + event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler CloseWindowRequested; event Windows.Foundation.TypedEventHandler RequestedThemeChanged; event Windows.Foundation.TypedEventHandler FocusModeChanged; diff --git a/src/cascadia/TerminalApp/TitlebarControl.cpp b/src/cascadia/TerminalApp/TitlebarControl.cpp index 1ed82f39ca..7ecf34d2c1 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.cpp +++ b/src/cascadia/TerminalApp/TitlebarControl.cpp @@ -7,8 +7,7 @@ #include "pch.h" #include "TitlebarControl.h" - -#include "ColorHelper.h" +#include "../../types/inc/ColorFix.hpp" #include "TitlebarControl.g.cpp" @@ -189,7 +188,8 @@ namespace winrt::TerminalApp::implementation return; } - const auto isBrightColor = ColorHelper::IsBrightColor(c); + constexpr auto lightnessThreshold = 0.6f; + const auto isBrightColor = ColorFix::GetLightness(c) >= lightnessThreshold; MinMaxCloseControl().RequestedTheme(isBrightColor ? winrt::Windows::UI::Xaml::ElementTheme::Light : winrt::Windows::UI::Xaml::ElementTheme::Dark); } diff --git a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj index 8e158d51da..282f68a589 100644 --- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj @@ -11,6 +11,7 @@ true true + Windows Terminal Main UI Library true @@ -35,7 +36,6 @@ - diff --git a/src/cascadia/TerminalApp/fzf/LICENSE b/src/cascadia/TerminalApp/fzf/LICENSE new file mode 100644 index 0000000000..04ac2144c3 --- /dev/null +++ b/src/cascadia/TerminalApp/fzf/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2013-2024 Junegunn Choi +Copyright (c) 2021-2025 Simon Hauser + +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. diff --git a/src/cascadia/TerminalApp/fzf/fzf.cpp b/src/cascadia/TerminalApp/fzf/fzf.cpp new file mode 100644 index 0000000000..6f2ac764cf --- /dev/null +++ b/src/cascadia/TerminalApp/fzf/fzf.cpp @@ -0,0 +1,432 @@ +#include "pch.h" +#include "fzf.h" + +#undef CharLower +#undef CharUpper + +using namespace fzf::matcher; + +constexpr int16_t ScoreMatch = 16; +constexpr int16_t ScoreGapStart = -3; +constexpr int16_t ScoreGapExtension = -1; +constexpr int16_t BoundaryBonus = ScoreMatch / 2; +constexpr int16_t NonWordBonus = ScoreMatch / 2; +constexpr int16_t CamelCaseBonus = BoundaryBonus + ScoreGapExtension; +constexpr int16_t BonusConsecutive = -(ScoreGapStart + ScoreGapExtension); +constexpr int16_t BonusFirstCharMultiplier = 2; +constexpr size_t npos = std::numeric_limits::max(); + +enum class CharClass : uint8_t +{ + NonWord = 0, + CharLower = 1, + CharUpper = 2, + Digit = 3, +}; + +static std::vector utf16ToUtf32(std::wstring_view text) +{ + const UChar* data = reinterpret_cast(text.data()); + int32_t dataLen = static_cast(text.size()); + int32_t cpCount = u_countChar32(data, dataLen); + + std::vector out(cpCount); + + UErrorCode status = U_ZERO_ERROR; + u_strToUTF32(out.data(), static_cast(out.size()), nullptr, data, dataLen, &status); + THROW_HR_IF(E_UNEXPECTED, status > U_ZERO_ERROR); + + return out; +} + +static void foldStringUtf32(std::vector& str) +{ + for (auto& cp : str) + { + cp = u_foldCase(cp, U_FOLD_CASE_DEFAULT); + } +} + +static size_t trySkip(const std::vector& input, const UChar32 searchChar, size_t startIndex) +{ + for (size_t i = startIndex; i < input.size(); ++i) + { + if (input[i] == searchChar) + { + return i; + } + } + return npos; +} + +// Unlike the equivalent in fzf, this one does more than Unicode. +static size_t asciiFuzzyIndex(const std::vector& input, const std::vector& pattern) +{ + size_t idx = 0; + size_t firstIdx = 0; + for (size_t pi = 0; pi < pattern.size(); ++pi) + { + idx = trySkip(input, pattern[pi], idx); + if (idx == npos) + { + return npos; + } + + if (pi == 0 && idx > 0) + { + firstIdx = idx - 1; + } + + idx++; + } + return firstIdx; +} + +static int16_t calculateBonus(CharClass prevClass, CharClass currentClass) +{ + if (prevClass == CharClass::NonWord && currentClass != CharClass::NonWord) + { + return BoundaryBonus; + } + if ((prevClass == CharClass::CharLower && currentClass == CharClass::CharUpper) || + (prevClass != CharClass::Digit && currentClass == CharClass::Digit)) + { + return CamelCaseBonus; + } + if (currentClass == CharClass::NonWord) + { + return NonWordBonus; + } + return 0; +} + +static constexpr auto s_charClassLut = []() { + std::array lut{}; + lut.fill(CharClass::NonWord); + lut[U_UPPERCASE_LETTER] = CharClass::CharUpper; + lut[U_LOWERCASE_LETTER] = CharClass::CharLower; + lut[U_MODIFIER_LETTER] = CharClass::CharLower; + lut[U_OTHER_LETTER] = CharClass::CharLower; + lut[U_DECIMAL_DIGIT_NUMBER] = CharClass::Digit; + return lut; +}(); + +static CharClass classOf(UChar32 ch) +{ + return s_charClassLut[u_charType(ch)]; +} + +static int32_t fzfFuzzyMatchV2(const std::vector& text, const std::vector& pattern, std::vector* pos) +{ + if (pattern.size() == 0) + { + return 0; + } + + auto foldedText = text; + foldStringUtf32(foldedText); + + size_t firstIndexOf = asciiFuzzyIndex(foldedText, pattern); + if (firstIndexOf == npos) + { + return 0; + } + + auto initialScores = std::vector(text.size()); + auto consecutiveScores = std::vector(text.size()); + auto firstOccurrenceOfEachChar = std::vector(pattern.size()); + auto bonusesSpan = std::vector(text.size()); + + int16_t maxScore = 0; + size_t maxScorePos = 0; + size_t patternIndex = 0; + size_t lastIndex = 0; + UChar32 firstPatternChar = pattern[0]; + UChar32 currentPatternChar = pattern[0]; + int16_t previousInitialScore = 0; + CharClass previousClass = CharClass::NonWord; + bool inGap = false; + + std::span lowerText(foldedText); + auto lowerTextSlice = lowerText.subspan(firstIndexOf); + auto initialScoresSlice = std::span(initialScores).subspan(firstIndexOf); + auto consecutiveScoresSlice = std::span(consecutiveScores).subspan(firstIndexOf); + auto bonusesSlice = std::span(bonusesSpan).subspan(firstIndexOf, text.size() - firstIndexOf); + + for (size_t i = 0; i < lowerTextSlice.size(); i++) + { + const auto currentChar = lowerTextSlice[i]; + const auto currentClass = classOf(text[i + firstIndexOf]); + const auto bonus = calculateBonus(previousClass, currentClass); + bonusesSlice[i] = bonus; + previousClass = currentClass; + + //currentPatternChar was already folded in ParsePattern + if (currentChar == currentPatternChar) + { + if (patternIndex < pattern.size()) + { + firstOccurrenceOfEachChar[patternIndex] = firstIndexOf + i; + patternIndex++; + if (patternIndex < pattern.size()) + { + currentPatternChar = pattern[patternIndex]; + } + } + lastIndex = firstIndexOf + i; + } + if (currentChar == firstPatternChar) + { + int16_t score = ScoreMatch + bonus * BonusFirstCharMultiplier; + initialScoresSlice[i] = score; + consecutiveScoresSlice[i] = 1; + if (pattern.size() == 1 && (score > maxScore)) + { + maxScore = score; + maxScorePos = firstIndexOf + i; + if (bonus == BoundaryBonus) + { + break; + } + } + inGap = false; + } + else + { + initialScoresSlice[i] = std::max(previousInitialScore + (inGap ? ScoreGapExtension : ScoreGapStart), 0); + consecutiveScoresSlice[i] = 0; + inGap = true; + } + previousInitialScore = initialScoresSlice[i]; + } + + if (patternIndex != pattern.size()) + { + return 0; + } + + if (pattern.size() == 1) + { + if (pos) + { + pos->push_back(maxScorePos); + } + return maxScore; + } + + const auto firstOccurrenceOfFirstChar = firstOccurrenceOfEachChar[0]; + const auto width = lastIndex - firstOccurrenceOfFirstChar + 1; + const auto rows = pattern.size(); + auto consecutiveCharMatrixSize = width * pattern.size(); + + std::vector scoreMatrix(width * rows); + std::copy_n(initialScores.begin() + firstOccurrenceOfFirstChar, width, scoreMatrix.begin()); + std::span scoreSpan(scoreMatrix); + + std::vector consecutiveCharMatrix(width * rows); + std::copy_n(consecutiveScores.begin() + firstOccurrenceOfFirstChar, width, consecutiveCharMatrix.begin()); + std::span consecutiveCharMatrixSpan(consecutiveCharMatrix); + + auto patternSliceStr = std::span(pattern).subspan(1); + + for (size_t off = 0; off < pattern.size() - 1; off++) + { + auto patternCharOffset = firstOccurrenceOfEachChar[off + 1]; + auto sliceLen = lastIndex - patternCharOffset + 1; + currentPatternChar = patternSliceStr[off]; + patternIndex = off + 1; + auto row = patternIndex * width; + inGap = false; + std::span textSlice = lowerText.subspan(patternCharOffset, sliceLen); + std::span bonusSlice(bonusesSpan.begin() + patternCharOffset, textSlice.size()); + std::span consecutiveCharMatrixSlice = consecutiveCharMatrixSpan.subspan(row + patternCharOffset - firstOccurrenceOfFirstChar, textSlice.size()); + std::span consecutiveCharMatrixDiagonalSlice = consecutiveCharMatrixSpan.subspan(row + patternCharOffset - firstOccurrenceOfFirstChar - 1 - width, textSlice.size()); + std::span scoreMatrixSlice = scoreSpan.subspan(row + patternCharOffset - firstOccurrenceOfFirstChar, textSlice.size()); + std::span scoreMatrixDiagonalSlice = scoreSpan.subspan(row + patternCharOffset - firstOccurrenceOfFirstChar - 1 - width, textSlice.size()); + std::span scoreMatrixLeftSlice = scoreSpan.subspan(row + patternCharOffset - firstOccurrenceOfFirstChar - 1, textSlice.size()); + + if (!scoreMatrixLeftSlice.empty()) + { + scoreMatrixLeftSlice[0] = 0; + } + + for (size_t j = 0; j < textSlice.size(); j++) + { + const auto currentChar = textSlice[j]; + const auto column = patternCharOffset + j; + const int16_t score = inGap ? scoreMatrixLeftSlice[j] + ScoreGapExtension : scoreMatrixLeftSlice[j] + ScoreGapStart; + int16_t diagonalScore = 0; + int16_t consecutive = 0; + if (currentChar == currentPatternChar) + { + diagonalScore = scoreMatrixDiagonalSlice[j] + ScoreMatch; + int16_t bonus = bonusSlice[j]; + consecutive = consecutiveCharMatrixDiagonalSlice[j] + 1; + if (bonus == BoundaryBonus) + { + consecutive = 1; + } + else if (consecutive > 1) + { + bonus = std::max({ bonus, BonusConsecutive, (bonusesSpan[column - consecutive + 1]) }); + } + if (diagonalScore + bonus < score) + { + diagonalScore += bonusSlice[j]; + consecutive = 0; + } + else + { + diagonalScore += bonus; + } + } + consecutiveCharMatrixSlice[j] = consecutive; + inGap = (diagonalScore < score); + int16_t cellScore = std::max(int16_t{ 0 }, std::max(diagonalScore, score)); + if (off + 2 == pattern.size() && cellScore > maxScore) + { + maxScore = cellScore; + maxScorePos = column; + } + scoreMatrixSlice[j] = cellScore; + } + } + + size_t currentColIndex = maxScorePos; + if (pos) + { + patternIndex = pattern.size() - 1; + bool preferCurrentMatch = true; + while (true) + { + const auto rowStartIndex = patternIndex * width; + const auto colOffset = currentColIndex - firstOccurrenceOfFirstChar; + const auto cellScore = scoreMatrix[rowStartIndex + colOffset]; + int32_t diagonalCellScore = 0; + int32_t leftCellScore = 0; + + if (patternIndex > 0 && currentColIndex >= firstOccurrenceOfEachChar[patternIndex]) + { + diagonalCellScore = scoreMatrix[rowStartIndex - width + colOffset - 1]; + } + if (currentColIndex > firstOccurrenceOfEachChar[patternIndex]) + { + leftCellScore = scoreMatrix[rowStartIndex + colOffset - 1]; + } + + if (cellScore > diagonalCellScore && + (cellScore > leftCellScore || (cellScore == leftCellScore && preferCurrentMatch))) + { + pos->push_back(currentColIndex); + if (patternIndex == 0) + { + break; + } + patternIndex--; + } + + currentColIndex--; + if (rowStartIndex + colOffset >= consecutiveCharMatrixSize) + { + break; + } + + preferCurrentMatch = (consecutiveCharMatrix[rowStartIndex + colOffset] > 1) || + ((rowStartIndex + width + colOffset + 1 < + consecutiveCharMatrixSize) && + (consecutiveCharMatrix[rowStartIndex + width + colOffset + 1] > 0)); + } + } + return maxScore; +} + +Pattern fzf::matcher::ParsePattern(const std::wstring_view patternStr) +{ + Pattern patObj; + size_t pos = 0; + + while (true) + { + const auto beg = patternStr.find_first_not_of(L' ', pos); + if (beg == std::wstring_view::npos) + { + break; // No more non-space characters + } + + const auto end = std::min(patternStr.size(), patternStr.find_first_of(L' ', beg)); + const auto word = patternStr.substr(beg, end - beg); + auto codePoints = utf16ToUtf32(word); + foldStringUtf32(codePoints); + patObj.terms.push_back(std::move(codePoints)); + pos = end; + } + + return patObj; +} + +std::optional fzf::matcher::Match(std::wstring_view text, const Pattern& pattern) +{ + if (pattern.terms.empty()) + { + return MatchResult{}; + } + + const auto textCodePoints = utf16ToUtf32(text); + + int32_t totalScore = 0; + std::vector allUtf32Pos; + + for (const auto& term : pattern.terms) + { + std::vector termPos; + auto score = fzfFuzzyMatchV2(textCodePoints, term, &termPos); + if (score <= 0) + { + return std::nullopt; + } + + totalScore += score; + allUtf32Pos.insert(allUtf32Pos.end(), termPos.begin(), termPos.end()); + } + + std::ranges::sort(allUtf32Pos); + allUtf32Pos.erase(std::ranges::unique(allUtf32Pos).begin(), allUtf32Pos.end()); + + std::vector runs; + std::size_t nextCodePointPos = 0; + size_t utf16Offset = 0; + + bool inRun = false; + size_t runStart = 0; + + for (size_t cpIndex = 0; cpIndex < textCodePoints.size(); cpIndex++) + { + const auto cp = textCodePoints[cpIndex]; + const size_t cpWidth = U16_LENGTH(cp); + + const bool isMatch = (nextCodePointPos < allUtf32Pos.size() && allUtf32Pos[nextCodePointPos] == cpIndex); + if (isMatch) + { + if (!inRun) + { + runStart = utf16Offset; + inRun = true; + } + nextCodePointPos++; + } + else if (inRun) + { + runs.push_back({ runStart, utf16Offset - 1 }); + inRun = false; + } + + utf16Offset += cpWidth; + } + + if (inRun) + { + runs.push_back({ runStart, utf16Offset - 1 }); + } + + return MatchResult{ totalScore, std::move(runs) }; +} diff --git a/src/cascadia/TerminalApp/fzf/fzf.h b/src/cascadia/TerminalApp/fzf/fzf.h new file mode 100644 index 0000000000..2af4b75f91 --- /dev/null +++ b/src/cascadia/TerminalApp/fzf/fzf.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace fzf::matcher +{ + struct TextRun + { + size_t Start; + size_t End; + }; + + struct MatchResult + { + int32_t Score = 0; + std::vector Runs; + }; + + struct Pattern + { + std::vector> terms; + }; + + Pattern ParsePattern(std::wstring_view patternStr); + std::optional Match(std::wstring_view text, const Pattern& pattern); +} diff --git a/src/cascadia/TerminalAzBridge/TerminalAzBridge.vcxproj b/src/cascadia/TerminalAzBridge/TerminalAzBridge.vcxproj index eada6967cb..29719f4850 100644 --- a/src/cascadia/TerminalAzBridge/TerminalAzBridge.vcxproj +++ b/src/cascadia/TerminalAzBridge/TerminalAzBridge.vcxproj @@ -11,6 +11,7 @@ false Windows Store Windows + Windows Terminal Azure Cloud Shell Connector diff --git a/src/cascadia/TerminalConnection/CTerminalHandoff.cpp b/src/cascadia/TerminalConnection/CTerminalHandoff.cpp index 01b25c9ffe..c132a74615 100644 --- a/src/cascadia/TerminalConnection/CTerminalHandoff.cpp +++ b/src/cascadia/TerminalConnection/CTerminalHandoff.cpp @@ -39,7 +39,7 @@ try ComPtr unk; RETURN_IF_FAILED(classFactory.As(&unk)); - RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &g_cTerminalHandoffRegistration)); + RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &g_cTerminalHandoffRegistration)); return S_OK; } @@ -83,41 +83,19 @@ HRESULT CTerminalHandoff::s_StopListening() // from the registered handler event function. HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE* in, HANDLE* out, HANDLE signal, HANDLE reference, HANDLE server, HANDLE client, const TERMINAL_STARTUP_INFO* startupInfo) { - try - { - // Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call. - // COM does not automatically clean that up for us. We must do it. - LOG_IF_FAILED(s_StopListening()); + // Report an error if no one registered a handoff function before calling this. + RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff); - // Report an error if no one registered a handoff function before calling this. - THROW_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff); - - // Call registered handler from when we started listening. - THROW_IF_FAILED(_pfnHandoff(in, out, signal, reference, server, client, startupInfo)); + // Call registered handler from when we started listening. + RETURN_IF_FAILED(_pfnHandoff(in, out, signal, reference, server, client, startupInfo)); #pragma warning(suppress : 26477) - TraceLoggingWrite( - g_hTerminalConnectionProvider, - "ReceiveTerminalHandoff_Success", - TraceLoggingDescription("successfully received a terminal handoff"), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + TraceLoggingWrite( + g_hTerminalConnectionProvider, + "ReceiveTerminalHandoff_Success", + TraceLoggingDescription("successfully received a terminal handoff"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - return S_OK; - } - catch (...) - { - const auto hr = wil::ResultFromCaughtException(); - -#pragma warning(suppress : 26477) - TraceLoggingWrite( - g_hTerminalConnectionProvider, - "ReceiveTerminalHandoff_Failed", - TraceLoggingDescription("failed while receiving a terminal handoff"), - TraceLoggingHResult(hr), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - - return hr; - } + return S_OK; } diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index 613ad50c91..f511ff8afa 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -30,8 +30,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { // Function Description: // - launches the client application attached to the new pseudoconsole - HRESULT ConptyConnection::_LaunchAttachedClient() noexcept - try + void ConptyConnection::_LaunchAttachedClient() { STARTUPINFOEX siEx{ 0 }; siEx.StartupInfo.cb = sizeof(STARTUPINFOEX); @@ -43,15 +42,16 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation auto attrList{ std::make_unique(size) }; #pragma warning(suppress : 26490) // We have to use reinterpret_cast because we allocated a byte array as a proxy for the adjustable size list. siEx.lpAttributeList = reinterpret_cast(attrList.get()); - RETURN_IF_WIN32_BOOL_FALSE(InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &size)); + THROW_IF_WIN32_BOOL_FALSE(InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &size)); - RETURN_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(siEx.lpAttributeList, - 0, - PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, - _hPC.get(), - sizeof(HPCON), - nullptr, - nullptr)); + THROW_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute( + siEx.lpAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, + _hPC.get(), + sizeof(HPCON), + nullptr, + nullptr)); auto cmdline{ wil::ExpandEnvironmentStringsW(_commandline.c_str()) }; // mutable copy -- required for CreateProcessW auto environment = _initialEnv; @@ -66,37 +66,87 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // WSLENV is a colon-delimited list of environment variables (+flags) that should appear inside WSL // https://devblogs.microsoft.com/commandline/share-environment-vars-between-wsl-and-windows/ - std::wstring wslEnv{ L"WT_SESSION:WT_PROFILE_ID:" }; + + // WSLENV.1: Get a handle to the WSLENV environment variable. + auto& wslEnv = environment.as_map()[L"WSLENV"]; + std::wstring additionalWslEnv; + + // WSLENV.2: Figure out what variables are already in WSLENV. + std::unordered_set wslEnvVars{ + // We never want to put a custom Windows PATH variable into WSLENV, + // because that would override WSL's computation of the NIX PATH. + L"PATH", + }; + for (const auto& part : til::split_iterator{ std::wstring_view{ wslEnv }, L':' }) + { + // Each part may contain a variable name and flags (e.g., /p, /l, etc.) + // We only care about the variable name for WSLENV. + const auto key = til::safe_slice_len(part, 0, part.rfind(L'/')); + wslEnvVars.emplace(key); + } + + // WSLENV.3: Add our terminal-specific environment variables to WSLENV. + static constexpr std::wstring_view builtinWslEnvVars[] = { + L"WT_SESSION", + L"WT_PROFILE_ID", + }; + // Misdiagnosis in MSVC 14.44.35207. No pointer arithmetic in sight. +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). + for (const auto& key : builtinWslEnvVars) + { + if (wslEnvVars.emplace(key).second) + { + additionalWslEnv.append(key); + additionalWslEnv.push_back(L':'); + } + } + + // add additional env vars if (_environment) { - // Order the environment variable names so that resolution order is consistent - std::set keys{}; for (const auto item : _environment) - { - keys.insert(std::wstring{ item.Key() }); - } - // add additional env vars - for (const auto& key : keys) { try { + const auto key = item.Key(); // This will throw if the value isn't a string. If that // happens, then just skip this entry. const auto value = winrt::unbox_value(_environment.Lookup(key)); - environment.set_user_environment_var(key.c_str(), value.c_str()); - // For each environment variable added to the environment, also add it to WSLENV - wslEnv += key + L":"; + environment.set_user_environment_var(key, value); + + // WSLENV.4: Add custom user environment variables to WSLENV. + if (wslEnvVars.emplace(key).second) + { + additionalWslEnv.append(key); + additionalWslEnv.push_back(L':'); + } } CATCH_LOG(); } } - // We want to prepend new environment variables to WSLENV - that way if a variable already - // exists in WSLENV but with a flag, the flag will be respected. - // (This behaviour was empirically observed) - wslEnv += environment.as_map()[L"WSLENV"]; - environment.as_map().insert_or_assign(L"WSLENV", wslEnv); + if (!additionalWslEnv.empty()) + { + // WSLENV.5: In the next step we'll prepend `additionalWslEnv` to `wslEnv`, + // so make sure that we have a single colon in between them. + const auto hasColon = additionalWslEnv.ends_with(L':'); + const auto needsColon = !wslEnv.starts_with(L':'); + if (hasColon != needsColon) + { + if (hasColon) + { + additionalWslEnv.pop_back(); + } + else + { + additionalWslEnv.push_back(L':'); + } + } + + // WSLENV.6: Prepend our additional environment variables to WSLENV. + wslEnv.insert(0, additionalWslEnv); + } } auto newEnvVars = environment.to_string(); @@ -114,7 +164,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation auto [newCommandLine, newStartingDirectory] = Utils::MangleStartingDirectoryForWSL(cmdline, _startingDirectory); const auto startingDirectory = newStartingDirectory.size() > 0 ? newStartingDirectory.c_str() : nullptr; - RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW( + THROW_IF_WIN32_BOOL_FALSE(CreateProcessW( nullptr, newCommandLine.data(), nullptr, // lpProcessAttributes @@ -141,10 +191,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation TraceLoggingWideString(_clientName.c_str(), "Client", "The attached client process"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - - return S_OK; } - CATCH_RETURN(); // Who decided that? #pragma warning(suppress : 26455) // Default constructor should not throw. Declare it 'noexcept' (f.6). @@ -313,6 +360,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation } CATCH_LOG() + try + { + auto processImageName{ wil::QueryFullProcessImageNameW(_piClient.hProcess) }; + _clientName = std::filesystem::path{ std::move(processImageName) }.filename().wstring(); + } + CATCH_LOG() + _pipe = std::move(pipe.server); *in = pipe.client.release(); *out = pipeClientClone.release(); @@ -359,7 +413,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility)); } - THROW_IF_FAILED(_LaunchAttachedClient()); + _LaunchAttachedClient(); } // But if it was an inbound handoff... attempt to synchronize the size of it with what our connection // window is expecting it to be on the first layout. @@ -556,13 +610,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation } } - void ConptyConnection::ClearBuffer() + void ConptyConnection::ClearBuffer(bool keepCursorRow) { // If we haven't connected yet, then we really don't need to do // anything. The connection should already start clear! if (_isConnected()) { - THROW_IF_FAILED(ConptyClearPseudoConsole(_hPC.get())); + THROW_IF_FAILED(ConptyClearPseudoConsole(_hPC.get(), keepCursorRow)); } } diff --git a/src/cascadia/TerminalConnection/ConptyConnection.h b/src/cascadia/TerminalConnection/ConptyConnection.h index 69edd7b7a8..cdcd99981f 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.h +++ b/src/cascadia/TerminalConnection/ConptyConnection.h @@ -25,7 +25,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation void Resize(uint32_t rows, uint32_t columns); void ResetSize(); void Close() noexcept; - void ClearBuffer(); + void ClearBuffer(bool keepCursorRow); void ShowHide(const bool show); @@ -58,7 +58,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation static HRESULT NewHandoff(HANDLE* in, HANDLE* out, HANDLE signal, HANDLE reference, HANDLE server, HANDLE client, const TERMINAL_STARTUP_INFO* startupInfo) noexcept; static winrt::hstring _commandlineFromProcess(HANDLE process); - HRESULT _LaunchAttachedClient() noexcept; + void _LaunchAttachedClient(); void _indicateExitWithStatus(unsigned int status) noexcept; static std::wstring _formatStatus(uint32_t status); void _LastConPtyClientDisconnected() noexcept; diff --git a/src/cascadia/TerminalConnection/ConptyConnection.idl b/src/cascadia/TerminalConnection/ConptyConnection.idl index e1ed83cc0c..5264238c79 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.idl +++ b/src/cascadia/TerminalConnection/ConptyConnection.idl @@ -15,7 +15,7 @@ namespace Microsoft.Terminal.TerminalConnection UInt16 ShowWindow { get; }; void ResetSize(); - void ClearBuffer(); + void ClearBuffer(Boolean keepCursorRow); void ShowHide(Boolean show); diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj index 8eda993772..2c7d276c50 100644 --- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj +++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj @@ -8,6 +8,7 @@ Console true true + Windows Terminal Connection Library true diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index a96adb7a1d..8a5b6b8d9a 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -104,8 +104,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation // GH#8969: pre-seed working directory to prevent potential races _terminal->SetWorkingDirectory(_settings->StartingDirectory()); - auto pfnCopyToClipboard = [this](auto&& PH1) { _terminalCopyToClipboard(std::forward(PH1)); }; - _terminal->SetCopyToClipboardCallback(pfnCopyToClipboard); + _terminal->SetCopyToClipboardCallback([this](wil::zwstring_view wstr) { + WriteToClipboard.raise(*this, winrt::make(winrt::hstring{ std::wstring_view{ wstr } }, std::string{}, std::string{})); + }); auto pfnWarningBell = [this] { _terminalWarningBell(); }; _terminal->SetWarningBellCallback(pfnWarningBell); @@ -174,8 +175,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation // // NOTE: Calling UpdatePatternLocations from a background // thread is a workaround for us to hit GH#12607 less often. - shared->outputIdle = std::make_unique>( - std::chrono::milliseconds{ 100 }, + shared->outputIdle = std::make_unique>( + til::throttled_func_options{ + .delay = std::chrono::milliseconds{ 100 }, + .debounce = true, + .trailing = true, + }, [this, weakThis = get_weak(), dispatcher = _dispatcher]() { dispatcher.TryEnqueue(DispatcherQueuePriority::Normal, [weakThis]() { if (const auto self = weakThis.get(); self && !self->_IsClosing()) @@ -195,8 +200,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation // If you rapidly show/hide Windows Terminal, something about GotFocus()/LostFocus() gets broken. // We'll then receive easily 10+ such calls from WinUI the next time the application is shown. - shared->focusChanged = std::make_unique>( - std::chrono::milliseconds{ 25 }, + shared->focusChanged = std::make_unique>( + til::throttled_func_options{ + .delay = std::chrono::milliseconds{ 25 }, + .debounce = true, + .trailing = true, + }, [this](const bool focused) { // Theoretically `debounced_func_trailing` should call `WaitForThreadpoolTimerCallbacks()` // with cancel=true on destruction, which should ensure that our use of `this` here is safe. @@ -204,9 +213,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation }); // Scrollbar updates are also expensive (XAML), so we'll throttle them as well. - shared->updateScrollBar = std::make_shared>( + shared->updateScrollBar = std::make_shared>( _dispatcher, - std::chrono::milliseconds{ 8 }, + til::throttled_func_options{ + .delay = std::chrono::milliseconds{ 8 }, + .trailing = true, + }, [weakThis = get_weak()](const auto& update) { if (auto core{ weakThis.get() }; core && !core->_IsClosing()) { @@ -588,7 +600,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation else if (vkey == VK_RETURN && !mods.IsCtrlPressed() && !mods.IsAltPressed()) { // [Shift +] Enter --> copy text - CopySelectionToClipboard(mods.IsShiftPressed(), false, nullptr); + CopySelectionToClipboard(mods.IsShiftPressed(), false, _settings->CopyFormatting()); _terminal->ClearSelection(); _updateSelectionUI(); return true; @@ -619,7 +631,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - vkey: The vkey of the key pressed. // - scanCode: The scan code of the key pressed. // - modifiers: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states. - // - keyDown: If true, the key was pressed, otherwise the key was released. + // - keyDown: If true, the key was pressed; otherwise, the key was released. bool ControlCore::TrySendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates modifiers, @@ -955,6 +967,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } + void ControlCore::SetHighContrastMode(const bool enabled) + { + _terminal->SetHighContrastMode(enabled); + } + Control::IControlSettings ControlCore::Settings() { return *_settings; @@ -1247,89 +1264,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _updateSelectionUI(); } - static wil::unique_close_clipboard_call _openClipboard(HWND hwnd) - { - bool success = false; - - // OpenClipboard may fail to acquire the internal lock --> retry. - for (DWORD sleep = 10;; sleep *= 2) - { - if (OpenClipboard(hwnd)) - { - success = true; - break; - } - // 10 iterations - if (sleep > 10000) - { - break; - } - Sleep(sleep); - } - - return wil::unique_close_clipboard_call{ success }; - } - - static void _copyToClipboard(const UINT format, const void* src, const size_t bytes) - { - wil::unique_hglobal handle{ THROW_LAST_ERROR_IF_NULL(GlobalAlloc(GMEM_MOVEABLE, bytes)) }; - - const auto locked = GlobalLock(handle.get()); - memcpy(locked, src, bytes); - GlobalUnlock(handle.get()); - - THROW_LAST_ERROR_IF_NULL(SetClipboardData(format, handle.get())); - handle.release(); - } - - static void _copyToClipboardRegisteredFormat(const wchar_t* format, const void* src, size_t bytes) - { - const auto id = RegisterClipboardFormatW(format); - if (!id) - { - LOG_LAST_ERROR(); - return; - } - _copyToClipboard(id, src, bytes); - } - - static void copyToClipboard(wil::zwstring_view text, std::string_view html, std::string_view rtf) - { - const auto clipboard = _openClipboard(nullptr); - if (!clipboard) - { - LOG_LAST_ERROR(); - return; - } - - EmptyClipboard(); - - if (!text.empty()) - { - // As per: https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats - // CF_UNICODETEXT: [...] A null character signals the end of the data. - // --> We add +1 to the length. This works because .c_str() is null-terminated. - _copyToClipboard(CF_UNICODETEXT, text.c_str(), (text.size() + 1) * sizeof(wchar_t)); - } - - if (!html.empty()) - { - _copyToClipboardRegisteredFormat(L"HTML Format", html.data(), html.size()); - } - - if (!rtf.empty()) - { - _copyToClipboardRegisteredFormat(L"Rich Text Format", rtf.data(), rtf.size()); - } - } - - // Called when the Terminal wants to set something to the clipboard, i.e. - // when an OSC 52 is emitted. - void ControlCore::_terminalCopyToClipboard(wil::zwstring_view wstr) - { - copyToClipboard(wstr, {}, {}); - } - // Method Description: // - Given a copy-able selection, get the selected text from the buffer and send it to the // Windows Clipboard (CascadiaWin32:main.cpp). @@ -1340,7 +1274,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // if we should defer which formats are copied to the global setting bool ControlCore::CopySelectionToClipboard(bool singleLine, bool withControlSequences, - const Windows::Foundation::IReference& formats) + const CopyFormat formats) { ::Microsoft::Terminal::Core::Terminal::TextCopyData payload; { @@ -1352,19 +1286,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation return false; } - // use action's copyFormatting if it's present, else fallback to globally - // set copyFormatting. - const auto copyFormats = formats != nullptr ? formats.Value() : _settings->CopyFormatting(); - - const auto copyHtml = WI_IsFlagSet(copyFormats, CopyFormat::HTML); - const auto copyRtf = WI_IsFlagSet(copyFormats, CopyFormat::RTF); + const auto copyHtml = WI_IsFlagSet(formats, CopyFormat::HTML); + const auto copyRtf = WI_IsFlagSet(formats, CopyFormat::RTF); // extract text from buffer // RetrieveSelectedTextFromBuffer will lock while it's reading payload = _terminal->RetrieveSelectedTextFromBuffer(singleLine, withControlSequences, copyHtml, copyRtf); } - copyToClipboard(payload.plainText, payload.html, payload.rtf); + WriteToClipboard.raise( + *this, + winrt::make( + winrt::hstring{ payload.plainText }, + std::move(payload.html), + std::move(payload.rtf))); return true; } @@ -2064,7 +1999,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _terminal->MultiClickSelection(terminalPosition, mode); selectionNeedsToBeCopied = true; } - else if (_settings->RepositionCursorWithMouse()) // This is also mode==Char && !shiftEnabled + else if (_settings->RepositionCursorWithMouse() && !selectionNeedsToBeCopied) // Don't reposition cursor if this is part of a selection operation { _repositionCursorWithMouse(terminalPosition); } @@ -2259,23 +2194,42 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void ControlCore::ClearBuffer(Control::ClearBufferType clearType) { - std::wstring_view command; - switch (clearType) - { - case ClearBufferType::Screen: - command = L"\x1b[H\x1b[2J"; - break; - case ClearBufferType::Scrollback: - command = L"\x1b[3J"; - break; - case ClearBufferType::All: - command = L"\x1b[H\x1b[2J\x1b[3J"; - break; - } - { const auto lock = _terminal->LockForWriting(); - _terminal->Write(command); + // In absolute buffer coordinates, including the scrollback (= Y is offset by the scrollback height). + const auto viewport = _terminal->GetViewport(); + // The absolute cursor coordinate. + const auto cursor = _terminal->GetViewportRelativeCursorPosition(); + + // GH#18732: Users want the row the cursor is on to be preserved across clears. + std::wstring sequence; + + if (clearType == ClearBufferType::Scrollback || clearType == ClearBufferType::All) + { + sequence.append(L"\x1b[3J"); + } + + if (clearType == ClearBufferType::Screen || clearType == ClearBufferType::All) + { + // Erase any viewport contents below (but not including) the cursor row. + if (viewport.Height() - cursor.y > 1) + { + fmt::format_to(std::back_inserter(sequence), FMT_COMPILE(L"\x1b[{};1H\x1b[J"), cursor.y + 2); + } + + // Erase any viewport contents above (but not including) the cursor row. + if (cursor.y > 0) + { + // An SU sequence would be simpler than this DL sequence, + // but SU isn't well standardized between terminals. + // Generally speaking, it's best avoiding it. + fmt::format_to(std::back_inserter(sequence), FMT_COMPILE(L"\x1b[H\x1b[{}M"), cursor.y); + } + + fmt::format_to(std::back_inserter(sequence), FMT_COMPILE(L"\x1b[1;{}H"), cursor.x + 1); + } + + _terminal->Write(sequence); } if (clearType == Control::ClearBufferType::Screen || clearType == Control::ClearBufferType::All) @@ -2284,8 +2238,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // Since the clearing of ConPTY occurs asynchronously, this call can result weird issues, // where a console application still sees contents that we've already deleted, etc. - // The correct way would be for ConPTY to emit the appropriate CSI n J sequences. - conpty.ClearBuffer(); + conpty.ClearBuffer(true); } } } @@ -2734,15 +2687,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - safe_void_coroutine ControlCore::_terminalCompletionsChanged(std::wstring_view menuJson, - unsigned int replaceLength) + void ControlCore::_terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength) { - auto args = winrt::make_self(winrt::hstring{ menuJson }, - replaceLength); - - co_await winrt::resume_background(); - - CompletionsChanged.raise(*this, *args); + CompletionsChanged.raise(*this, winrt::make(winrt::hstring{ menuJson }, replaceLength)); } // Select the region of text between [s.start, s.end), in buffer space diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 8e99e26155..3781055869 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -94,6 +94,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void UpdateSettings(const Control::IControlSettings& settings, const IControlAppearance& newAppearance); void ApplyAppearance(const bool focused); + void SetHighContrastMode(const bool enabled); Control::IControlSettings Settings(); Control::IControlAppearance FocusedAppearance() const; Control::IControlAppearance UnfocusedAppearance() const; @@ -123,7 +124,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void SendInput(std::wstring_view wstr); void PasteText(const winrt::hstring& hstr); - bool CopySelectionToClipboard(bool singleLine, bool withControlSequences, const Windows::Foundation::IReference& formats); + bool CopySelectionToClipboard(bool singleLine, bool withControlSequences, const CopyFormat formats); void SelectAll(); void ClearSelection(); bool ToggleBlockSelection(); @@ -276,6 +277,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event FontSizeChanged; til::typed_event TitleChanged; + til::typed_event WriteToClipboard; til::typed_event<> WarningBell; til::typed_event<> TabColorChanged; til::typed_event<> BackgroundColorChanged; @@ -306,9 +308,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation private: struct SharedState { - std::unique_ptr> outputIdle; - std::unique_ptr> focusChanged; - std::shared_ptr> updateScrollBar; + std::unique_ptr> outputIdle; + std::unique_ptr> focusChanged; + std::shared_ptr> updateScrollBar; }; void _setupDispatcherAndCallbacks(); @@ -324,7 +326,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _sendInputToConnection(std::wstring_view wstr); #pragma region TerminalCoreCallbacks - void _terminalCopyToClipboard(wil::zwstring_view wstr); void _terminalWarningBell(); void _terminalTitleChanged(std::wstring_view wstr); void _terminalScrollPositionChanged(const int viewTop, @@ -338,7 +339,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _terminalSearchMissingCommand(std::wstring_view missingCommand, const til::CoordType& bufferRow); void _terminalWindowSizeChanged(int32_t width, int32_t height); - safe_void_coroutine _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength); + void _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength); #pragma endregion #pragma region RendererCallbacks diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 528bc11458..a70573d956 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -102,6 +102,7 @@ namespace Microsoft.Terminal.Control IControlAppearance FocusedAppearance { get; }; IControlAppearance UnfocusedAppearance { get; }; Boolean HasUnfocusedAppearance(); + void SetHighContrastMode(Boolean enabled); UInt64 SwapChainHandle { get; }; @@ -184,6 +185,7 @@ namespace Microsoft.Terminal.Control // These events are called from some background thread event Windows.Foundation.TypedEventHandler TitleChanged; + event Windows.Foundation.TypedEventHandler WriteToClipboard; event Windows.Foundation.TypedEventHandler WarningBell; event Windows.Foundation.TypedEventHandler TabColorChanged; event Windows.Foundation.TypedEventHandler BackgroundColorChanged; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index f289912145..0190ac00d2 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -199,7 +199,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // if we should defer which formats are copied to the global setting bool ControlInteractivity::CopySelectionToClipboard(bool singleLine, bool withControlSequences, - const Windows::Foundation::IReference& formats) + const CopyFormat formats) { if (_core) { @@ -316,7 +316,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation else { // Try to copy the text and clear the selection - const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, false, nullptr); + const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, false, _core->Settings().CopyFormatting()); _core->ClearSelection(); if (_core->CopyOnSelect() || !successfulCopy) { @@ -461,7 +461,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // IMPORTANT! // DO NOT clear the selection here! // Otherwise, the selection will be cleared immediately after you make it. - CopySelectionToClipboard(false, false, nullptr); + CopySelectionToClipboard(false, false, _core->Settings().CopyFormatting()); } _singleClickTouchdownPos = std::nullopt; @@ -515,11 +515,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto ctrlPressed = modifiers.IsCtrlPressed(); const auto shiftPressed = modifiers.IsShiftPressed(); - if (ctrlPressed && shiftPressed) + if (ctrlPressed && shiftPressed && _core->Settings().ScrollToChangeOpacity()) { _mouseTransparencyHandler(delta); } - else if (ctrlPressed) + else if (ctrlPressed && !shiftPressed && _core->Settings().ScrollToZoom()) { _mouseZoomHandler(delta); } diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index dcf2c811d0..fc6a1bcb0c 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -84,7 +84,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool CopySelectionToClipboard(bool singleLine, bool withControlSequences, - const Windows::Foundation::IReference& formats); + const CopyFormat formats); void RequestPasteTextFromClipboard(); void SetEndSelectionPoint(const Core::Point pixelPosition); @@ -104,7 +104,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // // ControlCore::AttachUiaEngine receives a IRenderEngine as a raw pointer, which we own. // We must ensure that we first destroy the ControlCore before the UiaEngine instance - // in order to safely resolve this unsafe pointer dependency. Otherwise a deallocated + // in order to safely resolve this unsafe pointer dependency. Otherwise, a deallocated // IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown. // (C++ class members are destroyed in reverse order.) std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index a63726f2d3..bfd9470384 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -32,7 +32,7 @@ namespace Microsoft.Terminal.Control InteractivityAutomationPeer OnCreateAutomationPeer(); - Boolean CopySelectionToClipboard(Boolean singleLine, Boolean withControlSequences, Windows.Foundation.IReference formats); + Boolean CopySelectionToClipboard(Boolean singleLine, Boolean withControlSequences, CopyFormat formats); void RequestPasteTextFromClipboard(); void SetEndSelectionPoint(Microsoft.Terminal.Core.Point point); diff --git a/src/cascadia/TerminalControl/EventArgs.cpp b/src/cascadia/TerminalControl/EventArgs.cpp index b7e90a7511..e5a9fe5876 100644 --- a/src/cascadia/TerminalControl/EventArgs.cpp +++ b/src/cascadia/TerminalControl/EventArgs.cpp @@ -6,6 +6,7 @@ #include "FontSizeChangedArgs.g.cpp" #include "TitleChangedEventArgs.g.cpp" #include "ContextMenuRequestedEventArgs.g.cpp" +#include "WriteToClipboardEventArgs.g.cpp" #include "PasteFromClipboardEventArgs.g.cpp" #include "OpenHyperlinkEventArgs.g.cpp" #include "NoticeEventArgs.g.cpp" diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 526aa2b033..53f1245ef0 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -6,6 +6,7 @@ #include "FontSizeChangedArgs.g.h" #include "TitleChangedEventArgs.g.h" #include "ContextMenuRequestedEventArgs.g.h" +#include "WriteToClipboardEventArgs.g.h" #include "PasteFromClipboardEventArgs.g.h" #include "OpenHyperlinkEventArgs.g.h" #include "NoticeEventArgs.g.h" @@ -56,6 +57,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_PROPERTY(winrt::Windows::Foundation::Point, Position); }; + struct WriteToClipboardEventArgs : WriteToClipboardEventArgsT + { + WriteToClipboardEventArgs(winrt::hstring&& plain, std::string&& html, std::string&& rtf) : + _plain(std::move(plain)), + _html(std::move(html)), + _rtf(std::move(rtf)) + { + } + + winrt::hstring Plain() const noexcept { return _plain; } + winrt::com_array Html() noexcept { return _cast(_html); } + winrt::com_array Rtf() noexcept { return _cast(_rtf); } + + private: + static winrt::com_array _cast(const std::string& str) + { + const auto beg = reinterpret_cast(str.data()); + const auto len = str.size(); + return { beg, beg + len }; + } + + winrt::hstring _plain; + std::string _html; + std::string _rtf; + }; + struct PasteFromClipboardEventArgs : public PasteFromClipboardEventArgsT { public: diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 0cdaee9765..d6086fd922 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -6,6 +6,7 @@ namespace Microsoft.Terminal.Control [flags] enum CopyFormat { + None = 0x0, HTML = 0x1, RTF = 0x2, All = 0xffffffff @@ -31,6 +32,13 @@ namespace Microsoft.Terminal.Control AlphanumericHalfWidth, }; + enum WarnAboutMultiLinePaste + { + Automatic, + Always, + Never, + }; + runtimeclass FontSizeChangedArgs { Int32 Width { get; }; @@ -47,6 +55,13 @@ namespace Microsoft.Terminal.Control String Title; } + runtimeclass WriteToClipboardEventArgs + { + String Plain { get; }; // UTF-16, as required by CF_UNICODETEXT + byte[] Html { get; }; // UTF-8, as required by "HTML Format" + byte[] Rtf { get; }; // UTF-8, as required by "Rich Text Format" + } + runtimeclass PasteFromClipboardEventArgs { void HandleClipboardData(String data); diff --git a/src/cascadia/TerminalControl/HwndTerminalAutomationPeer.cpp b/src/cascadia/TerminalControl/HwndTerminalAutomationPeer.cpp index 6eae42a665..92724a2c67 100644 --- a/src/cascadia/TerminalControl/HwndTerminalAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/HwndTerminalAutomationPeer.cpp @@ -45,7 +45,7 @@ static std::wstring Sanitize(std::wstring_view text) // Arguments: // - text: the string we're validating // Return Value: -// - true, if the text is readable. false, otherwise. +// - true, if the text is readable; otherwise, false. static constexpr bool IsReadable(std::wstring_view text) { for (const auto c : text) diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl index ad621fdff8..c273574ed7 100644 --- a/src/cascadia/TerminalControl/IControlSettings.idl +++ b/src/cascadia/TerminalControl/IControlSettings.idl @@ -60,6 +60,8 @@ namespace Microsoft.Terminal.Control Boolean CopyOnSelect { get; }; Microsoft.Terminal.Control.CopyFormat CopyFormatting { get; }; Boolean FocusFollowMouse { get; }; + Boolean ScrollToZoom { get; }; + Boolean ScrollToChangeOpacity { get; }; String Commandline { get; }; String StartingDirectory { get; }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 7118c7a868..a2580db435 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -257,6 +257,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation return get_self(_termControl->_core); } + static Windows::UI::ViewManagement::AccessibilitySettings& _GetAccessibilitySettings() + { + static Windows::UI::ViewManagement::AccessibilitySettings accessibilitySettings; + return accessibilitySettings; + } + TermControl::TermControl(IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) : @@ -276,6 +282,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core = _interactivity.Core(); + // If high contrast mode was changed, update the appearance appropriately. + _core.SetHighContrastMode(_GetAccessibilitySettings().HighContrast()); + _revokers.HighContrastChanged = _GetAccessibilitySettings().HighContrastChanged(winrt::auto_revoke, [weakThis{ get_weak() }](const Windows::UI::ViewManagement::AccessibilitySettings& a11ySettings, auto&&) { + if (auto termControl = weakThis.get()) + { + termControl->_core.SetHighContrastMode(a11ySettings.HighContrast()); + termControl->_core.ApplyAppearance(termControl->_focused); + } + }); + // This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here. _revokers.RendererEnteredErrorState = _core.RendererEnteredErrorState(winrt::auto_revoke, { get_weak(), &TermControl::_RendererEnteredErrorState }); @@ -314,6 +330,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested }); _revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand }); _revokers.WindowSizeChanged = _core.WindowSizeChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleWindowSizeChanged }); + _revokers.WriteToClipboard = _core.WriteToClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleWriteToClipboard }); _revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard }); @@ -342,9 +359,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation // These three throttled functions are triggered by terminal output and interact with the UI. // Since Close() is the point after which we are removed from the UI, but before the // destructor has run, we MUST check control->_IsClosing() before actually doing anything. - _playWarningBell = std::make_shared( + _playWarningBell = std::make_shared>( dispatcher, - TerminalWarningBellInterval, + til::throttled_func_options{ + .delay = TerminalWarningBellInterval, + .leading = true, + }, [weakThis = get_weak()]() { if (auto control{ weakThis.get() }; control && !control->_IsClosing()) { @@ -352,9 +372,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation } }); - _updateScrollBar = std::make_shared>( + _updateScrollBar = std::make_shared>( dispatcher, - ScrollBarUpdateInterval, + til::throttled_func_options{ + .delay = ScrollBarUpdateInterval, + .trailing = true, + }, [weakThis = get_weak()](const auto& update) { if (auto control{ weakThis.get() }; control && !control->_IsClosing()) { @@ -1017,7 +1040,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - Set up each layer's brush used to display the control's background. // - Respects the settings for acrylic, background image and opacity from // _settings. - // * If acrylic is not enabled, setup a solid color background, otherwise + // * If acrylic is not enabled, set up a solid color background; otherwise, // use bgcolor as acrylic's tint // - Avoids image flickering and acrylic brush redraw if settings are changed // but the appropriate brush is still in place. @@ -1882,7 +1905,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - vkey: The vkey of the key pressed. // - scanCode: The scan code of the key pressed. // - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states. - // - keyDown: If true, the key was pressed, otherwise the key was released. + // - keyDown: If true, the key was pressed; otherwise, the key was released. bool TermControl::_TrySendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates modifiers, @@ -2612,7 +2635,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - withControlSequences: if enabled, the copied plain text contains color/style ANSI escape codes from the selection // - formats: which formats to copy (defined by action's CopyFormatting arg). nullptr // if we should defer which formats are copied to the global setting - bool TermControl::CopySelectionToClipboard(bool dismissSelection, bool singleLine, bool withControlSequences, const Windows::Foundation::IReference& formats) + bool TermControl::CopySelectionToClipboard(bool dismissSelection, bool singleLine, bool withControlSequences, const CopyFormat formats) { if (_IsClosing()) { @@ -2966,7 +2989,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - Adjusts given dimension (width or height) so that it aligns to the character grid. // The snap is always downward. // Arguments: - // - widthOrHeight: if true operates on width, otherwise on height + // - widthOrHeight: if true operates on width; otherwise, on height // - dimension: a dimension (width or height) to be snapped // Return Value: // - A dimension that would be aligned to the character grid. @@ -3180,7 +3203,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } CATCH_LOG(); - if (items.Size() > 0) + if (items && items.Size() > 0) { std::vector fullPaths; @@ -4131,7 +4154,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const IInspectable& /*args*/) { // formats = nullptr -> copy all formats - _interactivity.CopySelectionToClipboard(false, false, nullptr); + _interactivity.CopySelectionToClipboard(false, false, _core.Settings().CopyFormatting()); ContextMenu().Hide(); SelectionContextMenu().Hide(); } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 2e8fadd850..0aaae9c70f 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -60,7 +60,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring GetProfileName() const; - bool CopySelectionToClipboard(bool dismissSelection, bool singleLine, bool withControlSequences, const Windows::Foundation::IReference& formats); + bool CopySelectionToClipboard(bool dismissSelection, bool singleLine, bool withControlSequences, const CopyFormat formats); void PasteTextFromClipboard(); void SelectAll(); bool ToggleBlockSelection(); @@ -227,8 +227,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable); - - BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(WriteToClipboard, IInspectable, Control::WriteToClipboardEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); // clang-format on @@ -242,7 +242,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // // ControlCore::AttachUiaEngine receives a IRenderEngine as a raw pointer, which we own. // We must ensure that we first destroy the ControlCore before the UiaEngine instance - // in order to safely resolve this unsafe pointer dependency. Otherwise a deallocated + // in order to safely resolve this unsafe pointer dependency. Otherwise, a deallocated // IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown. // (C++ class members are destroyed in reverse order.) // Further, the TermControlAutomationPeer must be destructed after _uiaEngine! @@ -284,7 +284,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool _quickFixesAvailable{ false }; til::CoordType _quickFixBufferPos{}; - std::shared_ptr _playWarningBell; + std::shared_ptr> _playWarningBell; struct ScrollBarUpdate { @@ -294,7 +294,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation double newViewportSize; }; - std::shared_ptr> _updateScrollBar; + std::shared_ptr> _updateScrollBar; bool _isInternalScrollBarUpdate; @@ -362,7 +362,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation }; bool _InitializeTerminal(const InitializeReason reason); safe_void_coroutine _restoreInBackground(); - void _SetFontSize(int fontSize); void _TappedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e); void _KeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e); void _KeyUpHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e); @@ -394,8 +393,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _SwapChainSizeChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::SizeChangedEventArgs& e); void _SwapChainScaleChanged(const Windows::UI::Xaml::Controls::SwapChainPanel& sender, const Windows::Foundation::IInspectable& args); - void _TerminalTabColorChanged(const std::optional color); - void _ScrollPositionChanged(const IInspectable& sender, const Control::ScrollPositionChangedArgs& args); bool _CapturePointer(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); @@ -466,6 +463,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers; Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink; Control::ControlCore::TitleChanged_revoker TitleChanged; + Control::ControlCore::WriteToClipboard_revoker WriteToClipboard; Control::ControlCore::TabColorChanged_revoker TabColorChanged; Control::ControlCore::TaskbarProgressChanged_revoker TaskbarProgressChanged; Control::ControlCore::ConnectionStateChanged_revoker ConnectionStateChanged; @@ -480,6 +478,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // These are set up in _InitializeTerminal Control::ControlCore::RendererWarning_revoker RendererWarning; Control::ControlCore::SwapChainChanged_revoker SwapChainChanged; + Windows::UI::ViewManagement::AccessibilitySettings::HighContrastChanged_revoker HighContrastChanged; Control::ControlInteractivity::OpenHyperlink_revoker interactivityOpenHyperlink; Control::ControlInteractivity::ScrollPositionChanged_revoker interactivityScrollPositionChanged; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 9d72977455..2efe9a4106 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -53,6 +53,7 @@ namespace Microsoft.Terminal.Control Microsoft.Terminal.Control.IControlSettings Settings { get; }; event Windows.Foundation.TypedEventHandler TitleChanged; + event Windows.Foundation.TypedEventHandler WriteToClipboard; event Windows.Foundation.TypedEventHandler PasteFromClipboard; event Windows.Foundation.TypedEventHandler OpenHyperlink; event Windows.Foundation.TypedEventHandler SetTaskbarProgress; @@ -87,7 +88,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler CloseTerminalRequested; event Windows.Foundation.TypedEventHandler RestartTerminalRequested; - Boolean CopySelectionToClipboard(Boolean dismissSelection, Boolean singleLine, Boolean withControlSequences, Windows.Foundation.IReference formats); + Boolean CopySelectionToClipboard(Boolean dismissSelection, Boolean singleLine, Boolean withControlSequences, CopyFormat formats); void PasteTextFromClipboard(); void SelectAll(); Boolean ToggleBlockSelection(); diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp index 1adb9af5ce..90ad7b9876 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp @@ -52,7 +52,7 @@ static std::wstring Sanitize(std::wstring_view text) // Arguments: // - text: the string we're validating // Return Value: -// - true, if the text is readable. false, otherwise. +// - true, if the text is readable; otherwise, false. static constexpr bool IsReadable(std::wstring_view text) { for (const auto c : text) @@ -305,7 +305,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring TermControlAutomationPeer::GetNameCore() const { - // fallback to title if profile name is empty + // fall back to title if profile name is empty if (auto control{ _termControl.get() }) { const auto profileName = control->GetProfileName(); diff --git a/src/cascadia/TerminalControl/dll/TerminalControl.vcxproj b/src/cascadia/TerminalControl/dll/TerminalControl.vcxproj index e6cd662dd1..2af748bb22 100644 --- a/src/cascadia/TerminalControl/dll/TerminalControl.vcxproj +++ b/src/cascadia/TerminalControl/dll/TerminalControl.vcxproj @@ -21,6 +21,7 @@ projects compile properly when they depend on this "Microsoft.winmd." --> 3 + Windows Terminal Control Library diff --git a/src/cascadia/TerminalCore/ICoreAppearance.idl b/src/cascadia/TerminalCore/ICoreAppearance.idl index fa0364a322..d86fdd4262 100644 --- a/src/cascadia/TerminalCore/ICoreAppearance.idl +++ b/src/cascadia/TerminalCore/ICoreAppearance.idl @@ -23,7 +23,8 @@ namespace Microsoft.Terminal.Core { Never, Indexed, - Always + Always, + Automatic }; // TerminalCore declares its own Color struct to avoid depending diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 68d7178187..b292fd813b 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -94,7 +94,7 @@ void Terminal::UpdateSettings(ICoreSettings settings) if (_stateMachine) { - SetVtChecksumReportSupport(settings.AllowVtChecksumReport()); + SetOptionalFeatures(settings); } _getTerminalInput().ForceDisableWin32InputMode(settings.ForceVTInput()); @@ -143,7 +143,15 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance) renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBold, appearance.IntenseIsBold()); renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, appearance.IntenseIsBright()); - switch (appearance.AdjustIndistinguishableColors()) + // If AIC is set to Automatic, + // update the value based on if high contrast mode is enabled. + AdjustTextMode deducedAIC = appearance.AdjustIndistinguishableColors(); + if (deducedAIC == AdjustTextMode::Automatic) + { + deducedAIC = _highContrastMode ? AdjustTextMode::Indexed : AdjustTextMode::Never; + } + + switch (deducedAIC) { case AdjustTextMode::Always: renderSettings.SetRenderMode(RenderSettings::Mode::IndexedDistinguishableColors, false); @@ -213,16 +221,24 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance) _NotifyScrollEvent(); } +void Terminal::SetHighContrastMode(bool hc) noexcept +{ + _highContrastMode = hc; +} + void Terminal::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) { auto& engine = reinterpret_cast(_stateMachine->Engine()); engine.Dispatch().SetCursorStyle(cursorStyle); } -void Terminal::SetVtChecksumReportSupport(const bool enabled) +void Terminal::SetOptionalFeatures(winrt::Microsoft::Terminal::Core::ICoreSettings settings) { auto& engine = reinterpret_cast(_stateMachine->Engine()); - engine.Dispatch().SetVtChecksumReportSupport(enabled); + auto features = til::enumset{}; + features.set(ITermDispatch::OptionalFeature::ChecksumReport, settings.AllowVtChecksumReport()); + features.set(ITermDispatch::OptionalFeature::ClipboardWrite, settings.AllowVtClipboardWrite()); + engine.Dispatch().SetOptionalFeatures(features); } bool Terminal::IsXtermBracketedPasteModeEnabled() const noexcept @@ -453,7 +469,7 @@ void Terminal::TrySnapOnInput() // Parameters: // - // Return value: -// - true, if we are tracking mouse input. False, otherwise +// - true, if we are tracking mouse input; otherwise, false. bool Terminal::IsTrackingMouseInput() const noexcept { return _getTerminalInput().IsTrackingMouseInput(); @@ -466,7 +482,7 @@ bool Terminal::IsTrackingMouseInput() const noexcept // Parameters: // - // Return value: -// - true, if we are tracking mouse input. False, otherwise +// - true, if we are tracking mouse input; otherwise, false. bool Terminal::ShouldSendAlternateScroll(const unsigned int uiButton, const int32_t delta) const noexcept { @@ -583,7 +599,7 @@ std::optional Terminal::GetHyperlinkIntervalFromViewportPos // - vkey: The vkey of the last pressed key. // - scanCode: The scan code of the last pressed key. // - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states. -// - keyDown: If true, the key was pressed, otherwise the key was released. +// - keyDown: If true, the key was pressed; otherwise, the key was released. // Return Value: // - true if we translated the key event, and it should not be processed any further. // - false if we did not translate the key, and it should be processed into a character. @@ -905,7 +921,7 @@ void Terminal::_StoreKeyEvent(const WORD vkey, const WORD scanCode) noexcept // Arguments: // - scanCode: The scan code. // Return Value: -// - The key code matching the given scan code. Otherwise 0. +// - The key code matching the given scan code. Otherwise, 0. WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept { const auto codes = _lastKeyEventCodes.value_or(KeyEventCodes{}); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 1459c39fc3..837f1c8525 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -92,9 +92,10 @@ public: void UpdateSettings(winrt::Microsoft::Terminal::Core::ICoreSettings settings); void UpdateAppearance(const winrt::Microsoft::Terminal::Core::ICoreAppearance& appearance); + void SetHighContrastMode(bool hc) noexcept; void SetFontInfo(const FontInfo& fontInfo); void SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle); - void SetVtChecksumReportSupport(const bool enabled); + void SetOptionalFeatures(winrt::Microsoft::Terminal::Core::ICoreSettings settings); bool IsXtermBracketedPasteModeEnabled() const noexcept; std::wstring_view GetWorkingDirectory() noexcept; @@ -155,6 +156,7 @@ public: bool IsVtInputEnabled() const noexcept override; void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override; void NotifyBufferRotation(const int delta) override; + void NotifyShellIntegrationMark() override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; @@ -382,6 +384,7 @@ private: std::wstring _answerbackMessage; std::wstring _workingDirectory; + bool _highContrastMode = false; // This default fake font value is only used to check if the font is a raster font. // Otherwise, the font is changed to a real value with the renderer via TriggerFontChange. diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 7d8a706274..825712028e 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -377,7 +377,7 @@ void Terminal::NotifyBufferRotation(const int delta) auto selection{ _selection.write() }; wil::hide_name _selection; // If the end of the selection will be out of range after the move, we just - // clear the selection. Otherwise we move both the start and end points up + // clear the selection. Otherwise, we move both the start and end points up // by the given delta and clamp to the first row. if (selection->end.y < delta) { @@ -404,3 +404,9 @@ void Terminal::NotifyBufferRotation(const int delta) _NotifyScrollEvent(); } } + +void Terminal::NotifyShellIntegrationMark() +{ + // Notify the scrollbar that marks have been added so it can refresh the mark indicators + _NotifyScrollEvent(); +} diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index d588b67cb0..f0019d6614 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -438,7 +438,7 @@ void Terminal::ExpandSelectionToWord() // Arguments: // - dir: the direction we're scanning the buffer in to find the hyperlink of interest // Return Value: -// - true if we found a hyperlink to select (and selected it). False otherwise. +// - true if we found a hyperlink to select (and selected it); otherwise, false. void Terminal::SelectHyperlink(const SearchDirection dir) { if (_selectionMode != SelectionInteractionMode::Mark) diff --git a/src/cascadia/TerminalSettingsEditor/Actions.xaml b/src/cascadia/TerminalSettingsEditor/Actions.xaml index 3021bd607a..90a1118e57 100644 --- a/src/cascadia/TerminalSettingsEditor/Actions.xaml +++ b/src/cascadia/TerminalSettingsEditor/Actions.xaml @@ -56,7 +56,7 @@ @@ -97,7 +97,8 @@ @@ -159,11 +160,12 @@