Compare commits

..

73 Commits

Author SHA1 Message Date
Dustin L. Howett
795fdb043c Migrate spelling-0.0.21 changes from main 2021-05-24 08:33:20 -05:00
Dustin L. Howett
82406c7886 Migrate spelling-0.0.19 changes from main 2021-05-24 08:33:20 -05:00
Dustin L. Howett
d4b8b8bb6f Emit fixup debug info for internal tooling (#10151)
See MSFT-33187224 for more information.

This may impact debuggability; I have no idea how to tell.

(cherry picked from commit 89af44488f)
2021-05-24 11:50:04 -05:00
Michael Niksa
1db0b70143 Set keyword flags on all tracelog events (#10098)
Set keyword flags on all events so those sharing a provider with
telemetry do not fire unless tracing is enabled

## PR Checklist
* [x] Closes #10093
* [x] I work here
* [x] Tests passed
* [x] Documentation added in `til.h` about how keywords work and at the
  only other site of keywords we define in the Host project tracing
  files.

## Detailed Description of the Pull Request / Additional comments
I initially thought that we would need to split providers here to
accomplish this... but @DHowett helped me realize that might be a lot of
additional metadata and bloat binary size. So with help from a friend
from fundamentals, I realized that we could use Keywords to
differentiate here. We can no longer define 0 keywords as that
represents an any/all scenario. Every `TraceLoggingWrite` event now
needs a keyword. When our events have a keyword, they're not included in
any trace. Additionally, when we have an explicit keyword to check that
is different from the ones used for the telemetry pipeline, we can
ensure that we only do "hard work" to generate debug trace data when an
"ALL" type listener like TraceView or Windows Performance Recorder with
our profiles is listening to these providers for ALL keyworded events.

## Validation Steps Performed
- [x] - Built with full release build config to confirm performance is
  worse than dev builds BECAUSE of the telemetry event collector camping
  our provider and triggering full trace event generation on shared
  providers.
- [x] - Built with full release build config to enable statistics
  collection and validated trace event collection is excluded and trace
  event short-circuits work with this change.
- [x] - Checked that TraceView still sees both telemetry and tracing
  events
- [x] - Checked that WPR with our .wprp profile sees both telemetry and
  tracing events

(cherry picked from commit 66fdc645f7)

# Conflicts:
#	src/Terminal.wprp
#	src/cascadia/Remoting/Monarch.cpp
#	src/cascadia/Remoting/Peasant.cpp
#	src/cascadia/Remoting/WindowManager.cpp
#	src/cascadia/WindowsTerminal/IslandWindow.cpp
#	src/host/exe/exemain.cpp
2021-05-17 16:37:10 -05:00
Dustin L. Howett
661fde5937 Fix a number of shutdown crashes in TermControl (#10115)
1. The TSFInputControl may get a layout event after it has been removed
   from service (and no longer has a XAML tree)
   * Two fixes:
      * first, guard the layour updater from accessing detached xaml
	objects
      * second, shut down all pending throttled functions during close
	(not destruction!¹)
2. The TermControlAutomationPeer may be destructed before its events
   fire.
3. The TermControlAutomationPeer may receive a notification after it has
   been detached from XAML (and therefore has no dispatcher).

¹ Close happens before the control is removed from the XAML tree;
destruction happens some time later. We must detach all UI-bound events
in Close so that they don't fire between when we detach and when we
destruct.

Fixes MSFT-32496693
Fixes MSFT-32496158
Fixes MSFT-32509759
Fixes MSFT-32871913
2021-05-17 14:22:22 -05:00
Dustin Howett
791e8c8d3c Revert "In specific scenarios, focus the active control (#10048)"
This reverts commit aead759d93.
2021-05-14 12:55:00 -05:00
Mike Griese
aead759d93 In specific scenarios, focus the active control (#10048)
A redo of #6290. That PR was overkill. In that one, we'd toss focus back to the active control any time that the tab view item got focus. That's maybe not the _best_ solution.

Instead, this PR is precision strikes. We're re-using a lot of what we already have from #9260.
* When the context menu is closed, yeet focus to the control.
* When the renamer is dismissed, yeet focus to the control.
* When the TabViewItem is tapped (meaning no one else handled it), yeet focus to the control.

* [x] I work here
* [ ] This is UI so it doesn't have tests
* [x] Closes #3609
* [x] Closes #5750
* [x] Closes #6680

* [x] focus the window by clicking on the tab -> Control is focused.
* [x] Open the color picker with the context menu, can move the focus inside the picker with the arrow keys.
* [x] Dismiss the picker with esc -> Control is focused.
* [x] Dismiss the picker with enter -> Control is focused.
* [x] Dismiss the renamer with esc -> Control is focused.
* [x] Dismiss the renamer with enter -> Control is focused.
* [x] Dismiss the context menu with esc -> Control is focused.
* [x] Start renaming, then click on the tab -> Rename is committed, Control is focused.
* [x] Start renaming, then click on the text box -> focus is still in the text box

(cherry picked from commit 8564b269c4)
2021-05-14 12:42:05 -05:00
MPela
674c9182d7 Add Close menu items to the context menu flyout (#9859)
Add the "Close other tabs"/"Close tabs to the right" menu items straight to the tab context menu to work around #8238.
We can't add them into a dedicated sub-menu until the upstream crash is fixed.

* [X] Closes #8238
* [X] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan.

Moved the creation of the close menu items to a single function. Once the originating crash is fixed, the sub-menu can be restored by just replacing a few lines of code.

![immagine](https://user-images.githubusercontent.com/1140981/115059601-0dbc2480-9ee7-11eb-9889-d9ef8e6e7613.png)

(cherry picked from commit 2065fa7b76)
(cherry picked from commit c4e02e7274)
2021-05-14 12:41:48 -05:00
Dustin Howett
4cee91f843 Revert "[RELEASE ONLY] Remove the close submenu (#9102)"
This reverts commit c951a70208.
2021-05-14 12:23:15 -05:00
Leonard Hecker
ad45139bb4 Fix crash on exit introduced in ac265aa (#10042)
ControlCore::AttachUiaEngine receives a IRenderEngine as a raw pointer,
which TermControl owns. We must ensure that we first destroy the
ControlCore before the UiaEngine instance (both owned by TermControl).
Otherwise a deallocated IRenderEngine is accessed when
ControlCore calls Renderer::TriggerTeardown.

This crash was introduced in #10031.

* [x] I work here
* [x] Tests added/passed

* Run accevent.exe to cause a UiaEngine to be attached to a TermControl.
* Close the current tab
* Ensured no crashes occur

(cherry picked from commit 43040ef9d0)
2021-05-14 12:16:38 -05:00
Leonard Hecker
1721b91489 Backport hotfix ac265aa to v1.8 (#10033)
ControlCore's _renderer (IRenderTarget) is allocated as std::unique_ptr,
but is given to Terminal::CreateFromSettings as a reference.
ControlCore::Close deallocates the _renderer, but if ThrottledFuncs
are still scheduled to call ControlCore::UpdatePatternLocations
it'll cause Terminal::UpdatePatterns to be called, which in turn ends up
accessing the deallocated IRenderTarget reference and lead to a crash.

A proper solution with shared pointers is nontrivial and should be
attempted at a later point in time. This solution moves the teardown of
the _renderer into ControlCore::~ControlCore, where we can be certain
that no further strong references are held by ThrottledFuncs.

(cherry picked from commit 2c0889223a)
2021-05-14 11:30:19 -05:00
Don-Vito
37c802c6df Teach CmdPal search to use user locale (#9943)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9941
* [x] CLA signed.

## Detailed Description of the Pull Request / Additional comments
The bug is due to us using std::tolower, while the default locale is not user's locale.
The fix here is to use the same approach as upon sorting: lstrcmpi.
While there are additional methods to do locale aware comparison,
here we convert chars to string and call lstrcmpi.
While this approach seems somewhat inefficient it ensures consistency
(with the order of locales that lstrcmi tries to apply internally).

(cherry picked from commit cb55cec275)
2021-05-14 11:29:26 -05:00
MPela
e7ff7903ac Fix dropdown showing in up direction (#10009)
## Summary of the Pull Request
Let the dropdown menu open downwards if there's enough space, when clicking on the down arrow.

## PR Checklist
* [X] Closes #8924
* [X] CLA signed.

## Detailed Description of the Pull Request / Additional comments
Set the placement of the flyout to BottomEdgeAlignedLeft, as was done when opening the menu from the key binding.

## Validation Steps Performed
Manual tests

(cherry picked from commit 2b4c20bd6e)
2021-05-14 11:29:26 -05:00
Javier
2db6967521 [WPF] Allows setting the WPF control background when setting the terminal theme (#10026)
When syncing terminals across users (i.e. Liveshare shared terminals),
the terminal size is synced. This leads to having unused space around
the terminal which is the same color as the terminal's background
causing confusion as to what space is usable within the terminal.

Instead this change allows consumers to set the background color of the
control, separate from the terminal renderer's background, which makes
it easier to identify the edges of the terminal.

(cherry picked from commit 31414aa364)
2021-05-14 11:29:25 -05:00
Carlos Zamora
e8fff83307 Serialize stub for dynamic profiles (#9964)
#9962 was caused by a serialization bug. _Technically_, `ToJson` works
as intended: if the current layer has any values set, write them out to
the json. However, on first load, the dynamic profile `Profile` objects
are actually empty (because they inherit from base layer, then the
dynamic profile generator). This means that `ToJson` writes the dynamic
profiles as empty objects `{}`. Then, on reload, we see that the dynamic
profiles aren't in the JSON, and we write them again.

To get around this issue, we added a simple check to `Profile::ToJson`:
if we have a source, make sure we write out the name, guid, hidden, and
source. This is intended to align with `Profile::GenerateStub`.

Closes #9962

(cherry picked from commit 8f93f76214)
2021-05-14 11:29:25 -05:00
Don-Vito
e3ecc4d8da Fix tab and hyperlink tooltips to wrap long text (#9913)
* [x] Closes https://github.com/microsoft/terminal/issues/9869
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

(cherry picked from commit 6b4f70e985)
2021-05-14 11:29:21 -05:00
PankajBhojwani
c38696da5d Fix for configuring starting directory in SUI when defaults sets it to null (#9862)
## Summary of the Pull Request
Remove an unnecessary check in `Profiles.cpp` that was preventing us from enabling the text box and browse button when the user unchecks 'use parent process directory'

## PR Checklist
* [x] Closes #9847
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I work here

## Validation Steps Performed
Played around with it and it works.

(cherry picked from commit ad34291632)
2021-05-14 11:28:28 -05:00
Don-Vito
9e93ccbf32 Delay close tab on middle-click till pointer released (#9842)
* [x] Closes https://github.com/microsoft/terminal/issues/9836
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

Not sure what is the reason for handling right button.
But delaying it to PointerReleased seems not to regress anything.

(cherry picked from commit 05e7ea1423)
2021-05-14 11:28:24 -05:00
Don-Vito
16ce16e2f6 Fix profile name generation to allocate unique name (#9816)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9714
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

## Detailed Description of the Pull Request / Additional comments
Attempts to generate a name Profile X, where X is the index of the new profile (1-based).
As long as name is already taken, generates new name by incrementing X by 1

(cherry picked from commit 3368e602fd)
2021-05-14 11:27:18 -05:00
Don-Vito
0d451cce2d Limit terminal warning bells to one per second (#9812)
* [x] Closes https://github.com/microsoft/terminal/issues/9776
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

Use `ThrottledFunc` in `TermControl` to limit bell emission callback to one per second.

(cherry picked from commit 9a2d27e9f6)
2021-05-14 11:27:14 -05:00
Dustin L. Howett
c3b11bf827 Work around a compiler bug w/ coroutines and exceptions (#9893)
There is a bug in the compiler that we trip over when we handle the
exception generated by Package::Current inside a coroutine. It appears
to destruct an invalid instance of winrt::factory_guard_count.

Learned from the compiler folks: "coroutine frame pointer wasn't being
stored ... properly".

Fixes #9821

(cherry picked from commit 21b2e01643)
2021-04-19 16:15:26 -05:00
Kayla Cinnamon
45cde9edab doc: Add tabColor to JSON schema (#9843)
(cherry picked from commit 8bcb47339d)
2021-04-15 12:32:40 -05:00
Dustin L. Howett
3ac05363b1 Update {fmt} to 7.1.3 (#9580)
{fmt} 7.1.3 includes a number of changes, which I will summarize here:

* Switched to Dragonbox for float formatting (quoted, "20-30x faster"
  than ostringstream)
* Significantly improves `FMT_COMPILE` (compile-time format string)

(cherry picked from commit 544bd44851)
2021-04-13 17:19:13 -05:00
Evan Koschik
14a7e45280 Fix restore window position when exiting fullscreen (#9737)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

This change cleans up the Fullscreen implementation for both conhost and Terminal, improving the restore position (where the window goes when exiting fullscreen).

Prior to this change the window wasn't guaranteed to restore somewhere on the window's current monitor when exiting fullscreen. With this change the window will restore always to its current monitor, at a reasonable location (and will 'double restore' (to fullscreen->maximize->restore) after monitor changes while fullscreen, which is the expected user behavior.

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
## References

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #9746
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

A fullscreen window's monitor can change.
 - Win+Shift+left/right migrates a window between monitors.
 - User could open settings, display, and move the monitor or change its DPI.
 - The monitor could be unplugged.
 - The session could be remote and be disconnected.

A fullscreen window stores a 'restore position' when entering fullscreen, used to move the window back 'where it was'. BUT, its unexpected for the window to exit fullscreen and jump to another monitor. This means its previous position must be migrated from the old monitor's work area to the new monitor's work area.

If a window is maximized, it is sized to the work area. Like with fullscreen, a maximized window has a 'restore position', though unlike with fullscreen the restore position for maximized is stored by the system itself. Migration in cases where a maximized (or fullscreen) window's monitor changes is also taken care of by the system. To restore 'safely' to maximized (after changing window styles) a window must only `SetWindowPos(SWP_FRAMECHANGED)`. While technically a maximized window that becomes fullscreen 'is still maximized' (from Win32's perspective), its prudent to also `ShowWindow(SW_MAXIMIZED)` prior to `SWP_FRAMECHANGED` (to explicitly make the window maximized).

If not restoring to maximized, the restore position is adjusted by the new/ old work area. Additionally, the new/ old window DPI is used to adjust the size of the window by the DPI change (keeping the window's logical size the same).
 - The work area origin is checked first (shifting window rect by the change in origin)
 - The DPI is checked next, changing right/ bottom (size only)
 - Each edge of the window is compared against the corresponding edge of the work area, nudging the window back on-screen if hanging offscreen. By shifting right before left, bottom before top, the top-left is guaranteed on-screen.

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Tried it out. Seemed to work on my machine.
Jk, ran conhost/ terminal on mixed DPI system, max (or not), fullscreen, win+shift+left/ exit fullscreen/ maximize. Monitor unplug, etc.

(cherry picked from commit bc1ff0b71a)
2021-04-13 15:44:33 -05:00
Dustin L. Howett
6aa3d70091 Remove the splash screen (to save 100kb (compressed!)) (#9795)
We're a Centennial application; we can't even _use_ the splash screen.

(cherry picked from commit ab6f41f4bd)
2021-04-13 12:52:49 -05:00
Gabriel C
0284a917a6 Fix rightmost tab corner (#9575)
Before and after:
![image](https://user-images.githubusercontent.com/78622729/111928492-710b9000-8abc-11eb-9760-0ecd3eb038fb.png)

The bottom right corner appeared without a radius because of the custom padding.

(cherry picked from commit 8f16fdd817)
2021-04-13 11:02:53 -05:00
Eugene Samoylov
f67db399c1 [Settings UI] Clamp vintage cursor height and history size (#9370)
## Summary of the Pull Request
Add `Minimum` and `Maximum` for the cursor height numberbox in the SUI.
Add `Minimum` for the history size numberbox in the SUI.

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

## Validation Steps Performed
Manual validation

(cherry picked from commit ac3fecb134)
2021-04-13 10:19:08 -05:00
Dustin L. Howett
cddeb0898f Replace conhost's fmt::format strings with FMT_COMPILEd ones (#9581)
This reduces by 10% the binary size of OpenConsole x64 Release.

Note   | OpenConsole.exe
------ | ---------------------------
Before | 1156096
After  | 1037312
Delta  | -118784
%Delta | -10.27%

(cherry picked from commit d972ea2c28)
2021-04-13 09:48:39 -05:00
Dustin L. Howett
bea3e7f696 Command Palette: announce various mode and state changes to UIA (#9582)
This commit introduces a few different announcements to the command
palette.

When you delete the `>`, it will announce that you have entered
"command-line mode". When you reintroduce the `>`, it will announce that
you are in "action search mode."

When you enter a nested command, it will announce that you are looking
at "more options for new tab..." or "more options for select color
scheme...".

When you search and find nothing, it will announce that there were no
matching commands (or tabs!)

Related to #7907.

(cherry picked from commit da3f02a6e2)
2021-04-13 09:47:43 -05:00
Don-Vito
fadd1852d3 Prevent arrow keys from dismissing tab renamer (#9633)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9632
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

(cherry picked from commit 704836e45d)
2021-04-13 09:46:26 -05:00
Don-Vito
15028f399e Prevent mouse dragging from dismissing existing selection (#9790)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9787
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

## Validation Steps Performed
* [x] single click = no selection
* [x] single click and drag = selection starting from first point
* [x] single click in unfocused pane and drag = focus pane, selection starting from first point
* [x] double-click = selects a whole word
* [x] triple-click = selects a whole line
* [x] double-click and drag = selects a whole word, drag selects whole words
* [x] triple-click and drag = selects a whole line, drag selects whole lines
* [x] Shift single-click = defines start point
* [x] second Shift single-click = defines end point
* [x] Shift double-click = selects entire word
* [x] Shift triple-click = selects entire line
* [x] Shift double-click and drag = selects entire word, drag selects whole words
* [x] Mouse mode: Shift single-click = defines start point
* [x] Mouse mode: second Shift single-click = defines end point
* [x] Mouse mode: Shift double-click = selects entire word
* [x] Mouse mode: Shift triple-click = selects entire line
* [x] Mouse mode: Shift double-click and drag = selects entire word, drag selects whole words
* [x] With existing selection: single-click outside the selection and drag = establishes a new selection starting from the click point
* [x] Click-drag to set selection, shift-click-drag outside of selection = extend selection while dragging

(cherry picked from commit b8e36bae9f)
2021-04-13 09:08:18 -05:00
Dustin Howett
c00ae340db Merged PR 5903250: [Git2Git] Merged PR 5895926: conhost: fix two moderately high-hitting Watsons
This commit fixes two issues:

1. We were pushing ConsoleWaitBlocks into the wait queue before they
   were fully constructed. This resulted in the wait being called before
   it even had an API message in it. [MSFT-24113101]
2. Drawing DBCS characters and resizing (a lot) would cause a crash
   because of an invalid DBCS cell state. This hits in both Terminal and
   conhost. [MSFT-17364373] [GH-4907]

The DBCS state check was promoted from an NT_ASSERT (which never fired
in release) to a FAIL_FAST in !1794053. The console kept chugging along
without failing in fre for all those years.

Fixes MSFT-24113101
Fixes MSFT-17364373
Fixes GH-4907 Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_wdx_dxp_windev 108e746630749aa7851dd813b19e013ae31ef0db

(cherry picked from commit 8e7a866b06)
2021-04-13 09:06:57 -05:00
Dustin Howett
0c31c43cc4 Port the header generation changes from main
(cherry-picked from commit 03ea0f49ad)
2021-04-12 14:00:01 -05:00
Dustin L. Howett
806181db86 Propagate the hosting HWND to the new IFileDialogs (#9789)
This is required to maintain the modality of the dialogs, which we lost
when we moved from Pickers to IFileDialog. The HWND hosting Window API
we dreamed up is incompatible with IModalDialog, because IModalDialog
requires the HWND immediately upon `Show`. We're smuggling it in a
uint64, as is tradition.

zadjii-msft noticed this in #9760.

(cherry picked from commit 8f79f7c4c8)
2021-04-12 13:28:13 -05:00
hessedoneen
96930431c8 Bind Ctrl+Numpad Plus,Minus to the font size controls (#9753)
"ctrl+numpad_plus" command now increases font size and
"ctrl+numpad_minus" command now decreases font size.

Before this only "ctrl+=" and "ctrl+-" controlled font size. Increase in
font size follows previous convention where zooms in arbitrarily large,
but decrease in font size is capped.

## Validation Steps Performed
I first ran "ctrl+=" and "ctrl+-" in my terminal to verify its behavior,
then compared that against "ctrl+numpad_plus" and "ctrl+"numpad_minus".
Both increased and decreased the font size by the same amount, and both
appeared to have a cap for how small they could get, but did not appear
to have a cap for how big they could get.

Closes #7518

(cherry picked from commit 9a276c6371)
2021-04-12 13:28:13 -05:00
Don-Vito
17f9dbf7bb Trigger taskbar progress evaluation upon pane activation (#9779)
Trigger TaskbarProgressChanged every time we switch between active panes
(to update the tab header if required).

Closes #9743

(cherry picked from commit ca9e5e0fb0)
2021-04-12 13:28:13 -05:00
Don-Vito
aae9b83c42 Remove the icon grid column from the Actions page (#9780)
Closes #9715

(cherry picked from commit 912bd4dadb)
2021-04-12 13:28:11 -05:00
Don-Vito
e40b5e8ec1 Handle switch to tab binding even if tab doesn't exist (#9781)
Closes #9635

(cherry picked from commit a9a58f7156)
2021-04-12 13:26:43 -05:00
Breece W
842b162c85 Smoothen close caption button animation (#9763)
The red close button animation fades to gray then to transparent, when
standard behavior skips the gray part. I manually tested in light/dark/high
contrast mode.

Closes #9762

(cherry picked from commit d367c6b6b0)
2021-04-12 13:26:41 -05:00
Dustin L. Howett
a114d1b324 Replace Windows.Storage.Pickers with Common File Dialogs (#9760)
Using Pickers from an elevated application yields an
ERROR_ACCESS_DENIED. Of course it does: it was designed for the modern
app platform.

Using the common dialog infrastructure has some downsides¹, but it
doesn't crash and is just as flexible.

I've added some fun templated functions that help us with the
complexity.

Fixes #8957

¹You've got to use raw COM, and it runs in-proc instead of out-of-proc.

I tested every picker.

(cherry picked from commit 959c423e7a)
2021-04-12 13:24:20 -05:00
Dustin L. Howett
976a3c7d60 Give our NavigationView's acrylic a fallback color (#9752)
It will be a different color than the background, so it will look less
weird when it's unfocused. It also fixes the bug where the navigation
menu is transparent when acrylic is disabled systemwide.

Fixes #9337

(cherry picked from commit b310b1cffc)
2021-04-12 13:23:32 -05:00
Michael Niksa
dd1c3df54d Reduce instances of font fallback dialog (#9734)
Reduce instances of font fallback dialog through package font loading,
basic name trimming, and revised fallback test

- Adjusts the font dialog to only show when we attempt last-chance
  resolution from our hardcoded list of font names with a flag instead
  of with a string comparison by name
- Adds a resolution step to trim the font name by word from the end and
  retry to attempt to resolve a proper font that just has a weight
  suffix
- Adds a second font collection to font loading that will attempt to
  locate all TTF files sitting next to our binary, like in our package

- [x] Wrote my font preference in the JSON as `Cascadia Code Heavy` and
  watched it quietly resolve to just `Cascadia Code` without the dialog.
- [x] Put a font that isn't registered with the system into the layout
  directory for the package, set it as my desired font in Terminal, and
  watched it load just fine.
- [x] Try a font name with different casing and see if dialog doesn't
  pop anymore
- [x] Try a font with different (localized) names like MS ゴシック and
  see if dialog doesn't pop anymore
- [x] Check Win7 with WPF target

Closes #9375

(cherry picked from commit 7f5a19b627)
2021-04-12 13:23:32 -05:00
Dustin Howett
f8e8572c23 [STABLE ONLY] Combined revert of Environment Block Changes
Revert "Fix environment block creation (#7401)"

This reverts commit 7886f16714.

(cherry picked from commit e46ba65665)

Revert "Always create a new environment block before we spawn a process (#7243)"

This reverts commit 849243af99.

References #7418

(cherry picked from commit 4204d2535c)
2021-04-07 12:05:31 -05:00
Don-Vito
7b6625d7d9 Fix marking of new selection start (#9727)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9725
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

## Validation Steps Performed
* [x] single click = no selection
* [x] single click and drag = selection starting from first point
* [x] single click in unfocused pane and drag = focus pane, selection starting from first point
* [x] double-click = selects a whole word
* [x] triple-click = selects a whole line
* [x] double-click and drag = selects a whole word, drag selects whole words
* [x] triple-click and drag = selects a whole line, drag selects whole lines
* [x] Shift single-click = defines start point
* [x] second Shift single-click = defines end point
* [x] Shift double-click = selects entire word
* [x] Shift triple-click = selects entire line
* [x] Shift double-click and drag = selects entire word, drag selects whole words
* [x] Mouse mode: Shift single-click = defines start point
* [x] Mouse mode: second Shift single-click = defines end point
* [x] Mouse mode: Shift double-click = selects entire word
* [x] Mouse mode: Shift triple-click = selects entire line
* [x] Mouse mode: Shift double-click and drag = selects entire word, drag selects whole words
* [x] With existing selection: single-click outside the selection and drag = establishes a new selection starting from the click point

(cherry picked from commit e80e9b9e96)
2021-04-07 10:16:58 -05:00
Leonard Hecker
a8aa295757 Resolve circular reference in ThrottledFunc (#9729)
## Summary of the Pull Request

ThrottledFunc previously created a DispatcherTimer whose Tick callback holds a strong reference to the DispatcherTimer itself.
This causes a reference cycle, inadvertently leaking timer instances.

## PR Checklist

* [x] Closes #7710
* [x] I work here

## Detailed Description of the Pull Request / Additional comments

I've initially wanted to remove the `ThrottledFunc<>` optimization, but it turns out that this causes a 3% slowdown. That's definitely not a lot, but enough that we can just keep the optimization for the time being.
I've moved the implementation from the .cpp file into the header regardless since the two implementations are extremely similar and it's easier that way to keep them in line.

## Validation Steps Performed

I've ensured that the scrollbar still updates its length when I add new lines to a newly created tab.

(cherry picked from commit faf372f165)
2021-04-07 10:16:58 -05:00
Dustin L. Howett
979ef5d981 Move Branding into common props (#9668)
(cherry picked from commit 07c5735471)
2021-04-02 11:53:43 -05:00
Dustin L. Howett
0e68d0c09a Remove the shell extension's dependency on fmt for huge savings (#9552)
Yeah, this one-line change shaves off almost half the binary.

Binary size savings (x64 Release):

Note   | WindowsTerminalShellExt.dll
------ | ---------------------------
Before | 130048
After  | 73728
Delta  | -56320
%Delta | -43.3%

(cherry picked from commit 9069fcab79)
2021-04-02 11:53:43 -05:00
Carlos Zamora
823d28a8c6 Remove Base Layer from Settings UI (#9655)
Removes base layer (aka profiles.defaults) from the Settings UI. `SettingContainer` was also updated to not present a revert arrow when overriding a base layer value.

The new experience is now as follows:
- The revert arrow will only appear if you are overriding a value from a fragment extension.
- Users are still able to fully interact with `profiles.defaults` in their settings.json. Doing so still propagates those changes to their profiles as normal. In this case, the Settings UI presents the base layer value as the one that you selected.

Closes #9539

(cherry picked from commit 19fb9b21da)
2021-04-02 11:53:07 -05:00
Dustin L. Howett
aee33691bb Add some read and write locks around pattern tree manipulation (#9618)
We have been seeing some crashes (#9410) originating from a
use-after-free or a double-free in the renderer. The renderer is
iterating over the dirty rects from the render engine¹ and the rect list
is being freed out from under it.

Things like this are usually the result of somebody manipulating the
renderer's state outside of lock.

Therefore, this pull request introduces some targeted locking fixes
around manipulation of the pattern buffer (which, in turn, changes the
renderer state.)

¹ This was not a problem until #8621, which made the renderer return a
span instead of a copy for the list of dirty rects.

## Validation

I ran Terminal under App Verifier, and introduced a manul delay (under
lock) in the renderer such that the invalid map would definitely have
been invalidated between the renderer taking the lock and the renderer
handling the frame. AppVerif failed us without these locking changes,
and did not do so once they were introduced.

Closes #9410.

(cherry picked from commit ea3e56db81)
2021-04-02 11:52:40 -05:00
Don-Vito
3ca5374fe2 Fix wrong item template selection in CmdPal (#9487)
There seems to be a bug in WinUI (see microsoft/microsoft-ui-xaml#2121)
that results in heterogeneous `ModernCollectionBasePanel`  configured
with `DataTemplateSelector` and virtualization enabled to recycle a
container even if its `ContentTemplate` is wrong.

I considered few options of handling this:
* Disabling virtualization (by replacing item container template with
  some non-virtualizing panel (e.g., `StackPanel`,
  `VirtualizingStackPanel` with `VirtualizationMode`=`Standard`)
* Replacing `DataTemplateSelector` approach with `ChoosingItemContainer`
  event handling approach, which allows you to manage the item container
  (`ListViewItem`) allocation process.

I have chosen the last one, as it should limit the amount of
allocations, and might allow optimizations in the future.

The solution introduces:
* A container for `ListViewItem`s in the form of a map of sets:
  * The key of this map is a data template (e.g., `TabItemDataTemplate`)
  * The value in the set is the container
* `ChoosingItemContainer` event handler that looks for available item in
  the container or creates a new one
* `ContainerContentChanging` event handler that returns the recycled
  item to the container

Closes #9288

(cherry picked from commit e02d9a48e3)
2021-04-02 11:52:40 -05:00
Don-Vito
9e49ff2a05 Prevent tab context menu from closing root pane directly (#9571)
## Summary of the Pull Request
Currently a repeated attempt to close a read-only tab from context menu,
will bring the terminal into invalid state if user dismisses close action.

There are two root causes for this:
1. The tab close menu triggers the closing of the root pane
(rather than invoking close tab flow in the Terminal Page).
2. Currently panes are not aware that the closing was canceled,
and thus they trigger the Closed event, putting the system in a weird state,
where the Closed handlers were invoked, but the Pane remains.

This PR mitigates #9502, by addressing the first root cause
(the fix is trivial and hopefully can be serviced).
Moreover, it addresses the only existing UI flow that can trigger the issue.

The remaining problematic flow will occur when the connection is closed.
I have created a separate Issue to track it:
https://github.com/microsoft/terminal/issues/9572
as I guess the PR for it might be more complex.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9502
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

(cherry picked from commit a5ff7459b7)
2021-04-02 11:52:40 -05:00
Don-Vito
3ffefeb39f Fix read-only tab dialog to cancel tab closing when dismissed (#9573)
Currently dismissing "are you sure you wish to close read-only tab or pane"
dialog by pressing `ESC` will not abort tab closing
(aka the tab will be closed!)

The reason for this, is that we cancel, only if the "Cancel" is pressed
(aka result=PrimaryButton, while ESC returns result=None).

This PR fixes this, by doing what we usually do:
* Putting Cancel in the CloseButton (which is also triggered by ESC)
* Aborting the action if the result is not a Primary Button

However, since we want Cancel to be a default action,
we set CloseButton to be the DefaultButton  in XAML

(cherry picked from commit 9bd097f4ad)
2021-04-02 11:52:39 -05:00
Dustin L. Howett
80463833c1 Shell Extension: Remove C++/WinRT authoring dependency (#9525)
We don't need to use C++/WinRT's component authoring capabilities to be
a COM component. It's easier for us if we're not (and it makes the build
slightly faster!)

Binary size savings (x64 Release):

Note   | WindowsTerminalShellExt.dll
------ | ---------------------------
Before | 136192
After  | 130048
Delta  | 6144
%Delta | 4.5%

(cherry picked from commit acdcdcaccb)
2021-04-02 11:52:38 -05:00
Dustin L. Howett
396a698468 Disambiguate the shell extension CLSIDs to allow usage SXS (#9510)
Fixes #6416

(cherry picked from commit 1519236f2b)
2021-04-02 11:50:22 -05:00
PankajBhojwani
27bcdb03a2 Check if found folder is not null when loading fragments (#9477)
When loading fragments, check if the public folder is not null first

Closes #9473

(cherry picked from commit fbdfc4d446)
2021-03-15 18:29:59 -05:00
Don-Vito
a6af9a51ca Invalidate nested command with no valid subcommands (#9495)
Currently, when loading command with sub-commands that fail to parse,
we result with command that:
* Is not considered nested (has no sub-commands)
* Has no action of its own

The commit contains a few changes:
1. Protection in the dispatch that will prevent NPE
2. Change in the command parsing that will no load
a command if all its sub-commands failed to parse
3. We will add a warning in this case (the solution is somewhat
hacky, due to the hack that was there previously)

When such command is passed to a dispatch we crash with NPE.

Closes #9448

(cherry picked from commit 28da6c4ecb)
2021-03-15 18:29:58 -05:00
Eric Tian
1481475d2d Display correct tooltip when window is maximized (#9412)
## Summary of the Pull Request
Instead of displaying "Maximize" in the tooltip for the maximize/restore button even when the window is maximized, it now displays "Restore Down".

## References
Fixes #5693

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

## Validation Steps Performed
Tested manually.

(cherry picked from commit 8358f8d93f)
2021-03-15 18:29:58 -05:00
Don-Vito
2f81f5361c Fix selection on multi-click with no shift (#9455)
When working on #9403 I completely forgot that
double-click and triple-click should work even without shift.
Fixed it by allowing multi-selection even if not shift is pressed.

Closes #9453

## Validation Steps Performed
* [x] single click = no selection
* [x] single click and drag = selection starting from first point
* [x] single click in unfocused pane and drag = focus pane, selection starting from first point
* [x] double-click = selects a whole word
* [x] triple-click = selects a whole line
* [x] double-click and drag = selects a whole word, drag selects whole words
* [x] triple-click and drag = selects a whole line, drag selects whole lines
* [x] Shift single-click = defines start point
* [x] second Shift single-click = defines end point
* [x] Shift double-click = selects entire word
* [x] Shift triple-click = selects entire line
* [x] Shift double-click and drag = selects entire word, drag selects whole words
* [x] Mouse mode: Shift single-click = defines start point
* [x] Mouse mode: second Shift single-click = defines end point
* [x] Mouse mode: Shift double-click = selects entire word
* [x] Mouse mode: Shift triple-click = selects entire line
* [x] Mouse mode: Shift double-click and drag = selects entire word, drag selects whole words

(cherry picked from commit efc92de19f)
2021-03-15 18:29:58 -05:00
Don-Vito
2d53cb2f4d Allow interaction with hyperlinks in mouse mode (#9396)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9117
* [x] CLA signed.
* [x] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [x] I've discussed this with core contributors already.

## Detailed Description of the Pull Request / Additional comments
In mouse mode:
* Underline hyperlinks
* Activate hyperlink on ctrl+click rather than sending input to VT

(cherry picked from commit 6cd4e03a58)
2021-03-15 18:29:57 -05:00
Dustin L. Howett
42c01b83ea Update C++/WinRT to 2.0.210309.3 (#9437)
This update shrinks our binaries a little bit.

From a representative build on VS 16.8:

       | App     | Control | Connection | TSE     | WT     | Total   | msix (zip) |
       | --      | --      | --         | --      | --     | --      | --         |
Before | 2610176 | 1006592 | 433152     | 1352192 | 321536 | 5723648 | 8336910    |
After  | 2532352 | 986624  | 431104     | 1312768 | 313344 | 5576192 | 8287648    |
Delta  | 77824   | 19968   | 2048       | 39424   | 8192   | 147456  | 49262      |
%Delta | 2.98%   | 1.98%   | 0.47%      | 2.92%   | 2.55%  | 2.58%   | 0.59%      |

(cherry picked from commit 3029bb8a68)
2021-03-15 18:29:54 -05:00
Don-Vito
03b373f254 Fix selection logic with shift on multi-click (#9403)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9382
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

## Detailed Description of the Pull Request / Additional comments
The selection with shift is quite broken in 1.6.

It started with #8611 that introduces cell selection on `shift+click`.
This change resulted in the following defect:
`shift+double-click`, `shift+triple-click` select only parts  of the word.
The reason for this is that the first `shift+click` establishes the selection,
while the consequent clicks simply extend it to the relevant boundary
(aka word / line boundary)

However, the logic was broken even before #8611.
For instance, `shift+triple-click` had exactly the same handicap:
`shift+double-click` was establishing the selection and the
third click was simply extending it to the line boundary.

This PR addresses the both defects in the following manner:
upon multi-click that starts new selection we establish
a new selection on every consequent click using appropriate mode
(cell/word/line) rather than trying to extend one.
For this purpose we remember the position that started the selection.

(cherry picked from commit 83f2a3bb3d)
2021-03-15 18:29:11 -05:00
Don-Vito
c7dc350504 Fix TermControl initialization to pre-seed working dir (#9397)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/8969
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

(cherry picked from commit 19bd0c94e7)
2021-03-15 18:29:11 -05:00
Don-Vito
7f1bdaf962 Fix overflow in scroll-to-bottom (#9389)
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/9353
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.

(cherry picked from commit 1202f89399)
2021-03-15 18:29:11 -05:00
Carlos Zamora
fb4dab0143 Change SettingsTab close icon to be smaller (#9324)
We were using the wrong close icon for the settings tab. This is a side effect of `TerminalTab` overriding `TabBase`'s implementation.

#6800 - Settings UI Epic

Closes #9317

(cherry picked from commit 4a95341caf)
2021-03-15 18:29:10 -05:00
PankajBhojwani
77f181febd Fix pixel shaders not loading (#9371)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Fix for #9354

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #9354
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I work here

(cherry picked from commit 5aaf5b4d59)
2021-03-15 18:29:10 -05:00
Dustin Howett
89c9e6db84 [PICK] Cherry-pick loc key from 83d35c119
(cherry picked from commit 95f63a9100)
2021-02-25 14:48:51 -08:00
Dustin L. Howett
f5f330a019 Update Cascadia Code to 2102.25 (#9295)
This update fixes some issues in Cascadia Code's February update:

microsoft/cascadia-code#406 - updated anchor type to lock with the other equals-related ligatures
microsoft/cascadia-code#408 - corrected component used for glyph to align with Unicode
microsoft/cascadia-code#412 - updated locl features removing iacute_j ligature and Catalan substitution
microsoft/cascadia-code#414 - increased overlaps of middle glyph for arrow ligatures
microsoft/cascadia-code#415 - reduces width of macronbelow
microsoft/cascadia-code#416 - rolls back name ID 4 modification as JetBrains cannot process it correctly
microsoft/cascadia-code#428 - rolls back variation of the underline to prevent MVAR table generation

Full changelist:
* Repositioned tilde in related ligatures. Previously it was higher than the standard one.
* Added missing vietnamese anchors on acute and grave (futureproofing).
* Corrected / made consistent greater & less positioning in </> and <$> related ligatures.
* Otherwise reviewed hinting

(cherry picked from commit cb03b97e67)
2021-02-25 14:48:22 -08:00
PankajBhojwani
ec89b85462 Fix updating with fragments ignoring original profile settings (#9293)
Turns out we were adding the fragment source to profiles we update. This
PR fixes it so we keep the original source.

## Validation Steps Performed
Existing profile settings are maintained

Closes #9290

(cherry picked from commit f26c246c7c)
2021-02-25 14:48:21 -08:00
Carlos Zamora
e295cc4ae2 Update dropdown's settings keyboard accelerator (#9294)
Fixes a bug introduced by #9224 where the wrong keyboard accelerator
would appear in the new tab dropdown. We were looking for the "settings
file" version of the action, as opposed to the "settings UI" version.

## References
#9224 - Settings UI as default
#6800 - Settings UI Epic

(cherry picked from commit 649c546960)
2021-02-25 14:48:21 -08:00
PankajBhojwani
e103ca59e4 Fix settings not updating on reload (#9289)
In #8602, we started passing a child of the `TerminalSettings` to the
control upon tab initialization, but forgot to do the same when new
controls get created on a pane split.

## Validation Steps Performed
Settings reload with multiple panes works

Closes #9280

(cherry picked from commit a930aa390e)
2021-02-25 14:48:21 -08:00
Carlos Zamora
28c7890cbc Add help text to 'more options' in command palette (#9271)
Similar to #9262. This creates another data template specifically for
command palette items that open up more options. We leverage the
localization key from #9262 to apply help text to this template
automatically.

Using the data template approach, we now have no need for the
`HasNestedComandsVisibilityConverter`, so that set of files is now
deleted. The logic to detect nested commands was moved to the template
selector.

## Validation Steps Performed
Tested using NVDA.

Addresses #7908 better

(cherry picked from commit d1bf0fcd1e)
2021-02-24 12:02:19 -08:00
Dustin L. Howett
4dee531aee Exclude MonarchPeasantPackage from AnyCPU/DotNet*Test configs (#9272)
Visual Studio automatically enabled this package to build in all
configurations. This results in a build error when we go to pack the WPF
control.

(cherry picked from commit 66033dcb01)
2021-02-24 11:34:58 -08:00
Dustin L. Howett
c951a70208 [RELEASE ONLY] Remove the close submenu (#9102)
There's a platform issue that causes it to crash.
Fixes #8944.

(cherry picked from commit 5fdd1560fd)
2021-02-23 16:41:36 -08:00
205 changed files with 5260 additions and 5130 deletions

View File

@@ -5,7 +5,7 @@
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
<XesBaseYearForStoreVersion>2021</XesBaseYearForStoreVersion>
<VersionMajor>1</VersionMajor>
<VersionMinor>8</VersionMinor>
<VersionMinor>7</VersionMinor>
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
</PropertyGroup>
</Project>

View File

@@ -1137,6 +1137,11 @@
"type": "boolean",
"default": false
},
"tabColor": {
"$ref": "#/definitions/Color",
"description": "Sets the color of the profile's tab. Using the tab color picker will override this color.",
"type": ["string", "null"]
},
"tabTitle": {
"description": "If set, will replace the name as the title to pass to the shell on startup. Some shells (like bash) may choose to ignore this initial value, while others (cmd, powershell) may use this value over the lifetime of the application.",
"type": ["string", "null"]

View File

@@ -4,7 +4,7 @@
"type": "git",
"git": {
"repositoryUrl": "https://github.com/fmtlib/fmt",
"commitHash": "f19b1a521ee8b606dedcadfda69fd10ddf882753"
"commitHash": "7bdf0628b1276379886c7f6dda2cef2b3b374f0b"
}
}
}

View File

@@ -72,43 +72,27 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
if (detail::const_check(F::is_signed && !T::is_signed)) {
// From may be negative, not allowed!
if (fmt::detail::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>((T::max)())) {
ec = 1;
return {};
}
if (F::digits > T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>((T::max)())) {
// outside range.
ec = 1;
return {};
}
}
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
return static_cast<To>(from); // Lossless conversion.
}
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
@@ -190,11 +174,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
if (ec) return {};
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
if (detail::const_check(Factor::num != 1)) {
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
@@ -209,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
return ec ? To() : To(tocount);
}
/**
@@ -351,6 +325,11 @@ inline std::tm localtime(std::time_t time) {
return lt.tm_;
}
inline std::tm localtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return localtime(std::chrono::system_clock::to_time_t(time_point));
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct dispatcher {
@@ -387,6 +366,11 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_;
}
inline std::tm gmtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return gmtime(std::chrono::system_clock::to_time_t(time_point));
}
namespace detail {
inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) {
@@ -399,6 +383,17 @@ inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
}
} // namespace detail
template <typename Char>
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
: formatter<std::tm, Char> {
template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock> val,
FormatContext& ctx) -> decltype(ctx.out()) {
std::tm time = localtime(val);
return formatter<std::tm, Char>::format(time, ctx);
}
};
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {

View File

@@ -463,16 +463,16 @@ template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
}
template <typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
}
template <typename Char>
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
@@ -496,7 +496,7 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
@@ -504,20 +504,22 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
}
/**
\rst
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
Example:
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
detail::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
@@ -558,7 +560,42 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return vformat(ts, to_string_view(format_str),
detail::make_args_checked<Args...>(format_str, args...));
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -368,7 +368,8 @@ template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
@@ -406,6 +407,19 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
return {{&s[pos], size}};
}
template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value);
}
};
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
@@ -430,7 +444,9 @@ template <typename Char, typename T, int N> struct spec_field {
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
basic_format_context<OutputIt, Char> ctx(out, {});
const auto& vargs =
make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(arg, ctx);
}
};
@@ -489,16 +505,17 @@ constexpr auto parse_tail(T head, S format_str) {
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos) {
size_t pos, int arg_id) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str);
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1};
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
}
// Compiles a non-empty format string and returns the compiled representation
@@ -518,8 +535,8 @@ constexpr auto compile_format_string(S format_str) {
format_str);
} else if constexpr (str[POS + 1] == ':') {
using type = get_type<ID, Args>;
constexpr auto result = parse_specs<type>(str, POS + 2);
return parse_tail<Args, result.end, ID + 1>(
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else {
return unknown_format();
@@ -530,8 +547,13 @@ constexpr auto compile_format_string(S format_str) {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
}
}
}
@@ -587,8 +609,7 @@ template <typename CompiledFormat, typename... Args,
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
basic_memory_buffer<Char> buffer;
detail::buffer<Char>& base = buffer;
cf.format(std::back_inserter(base), args...);
cf.format(detail::buffer_appender<Char>(buffer), args...);
return to_string(buffer);
}
@@ -608,8 +629,7 @@ template <typename CompiledFormat, typename... Args,
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::buffer<Char>& base = buffer;
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
@@ -618,9 +638,13 @@ template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
#ifdef __cpp_if_constexpr
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
}
#endif
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
@@ -643,18 +667,30 @@ OutputIt format_to(OutputIt out, const S&, const Args&... args) {
return format_to(out, compiled, args...);
}
template <
typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
detail::basic_compiled_format, CompiledFormat>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
template <typename OutputIt, typename CompiledFormat, typename... Args>
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
const Args&... args) ->
typename std::enable_if<
detail::is_output_iterator<OutputIt,
typename CompiledFormat::char_type>::value &&
std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value,
format_to_n_result<OutputIt>>::type {
auto it =
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(detail::counting_iterator(), cf, args...).count();

View File

@@ -18,7 +18,7 @@
#include <vector>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 70001
#define FMT_VERSION 70103
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
@@ -57,6 +57,7 @@
# define FMT_MSC_VER 0
# define FMT_SUPPRESS_MSC_WARNING(n)
#endif
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
#else
@@ -64,7 +65,7 @@
#endif
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
!(FMT_ICC_VERSION && FMT_ICC_VERSION < 1600)
(!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
# define FMT_HAS_INCLUDE(x) __has_include(x)
#else
# define FMT_HAS_INCLUDE(x) 0
@@ -99,7 +100,7 @@
#endif
#ifndef FMT_OVERRIDE
# if FMT_HAS_FEATURE(cxx_override) || \
# if FMT_HAS_FEATURE(cxx_override_control) || \
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
# define FMT_OVERRIDE override
# else
@@ -152,7 +153,7 @@
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
# define FMT_DEPRECATED [[deprecated]]
# else
# if defined(__GNUC__) || defined(__clang__)
# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
# define FMT_DEPRECATED __attribute__((deprecated))
# elif FMT_MSC_VER
# define FMT_DEPRECATED __declspec(deprecated)
@@ -177,9 +178,17 @@
# endif
#endif
#ifndef FMT_BEGIN_NAMESPACE
#ifndef FMT_USE_INLINE_NAMESPACES
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900
(FMT_MSC_VER >= 1900 && !_MANAGED)
# define FMT_USE_INLINE_NAMESPACES 1
# else
# define FMT_USE_INLINE_NAMESPACES 0
# endif
#endif
#ifndef FMT_BEGIN_NAMESPACE
# if FMT_USE_INLINE_NAMESPACES
# define FMT_INLINE_NAMESPACE inline namespace
# define FMT_END_NAMESPACE \
} \
@@ -269,8 +278,7 @@ struct monostate {};
namespace detail {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
// A helper function to suppress "conditional expression is constant" warnings.
template <typename T> constexpr T const_check(T value) { return value; }
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
@@ -299,7 +307,8 @@ template <typename T> struct std_string_view {};
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
!(FMT_CLANG_VERSION && FMT_MSC_VER)
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
@@ -506,6 +515,18 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
using type = typename result::value_type;
};
// Reports a compile-time error if S is not a valid format string.
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
FMT_INLINE void check_format_string(const S&) {
#ifdef FMT_ENFORCE_COMPILE_STRING
static_assert(is_compile_string<S>::value,
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
"FMT_STRING.");
#endif
}
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
struct error_handler {
constexpr error_handler() = default;
constexpr error_handler(const error_handler&) = default;
@@ -545,8 +566,9 @@ class basic_format_parse_context : private ErrorHandler {
using iterator = typename basic_string_view<Char>::iterator;
explicit constexpr basic_format_parse_context(
basic_string_view<Char> format_str, ErrorHandler eh = {})
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
basic_string_view<Char> format_str, ErrorHandler eh = {},
int next_arg_id = 0)
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
/**
Returns an iterator to the beginning of the format string range being
@@ -616,8 +638,24 @@ template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
// Checks whether T is a container with contiguous storage.
template <typename T> struct is_contiguous : std::false_type {};
template <typename Char>
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
namespace detail {
// Extracts a reference to the container from back_insert_iterator.
template <typename Container>
inline Container& get_container(std::back_insert_iterator<Container> it) {
using bi_iterator = std::back_insert_iterator<Container>;
struct accessor : bi_iterator {
accessor(bi_iterator iter) : bi_iterator(iter) {}
using bi_iterator::container;
};
return *accessor(it).container;
}
/**
\rst
A contiguous memory buffer with an optional growing ability. It is an internal
@@ -640,6 +678,8 @@ template <typename T> class buffer {
size_(sz),
capacity_(cap) {}
~buffer() = default;
/** Sets the buffer data and capacity. */
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
ptr_ = buf_data;
@@ -655,7 +695,6 @@ template <typename T> class buffer {
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
virtual ~buffer() = default;
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
@@ -675,24 +714,26 @@ template <typename T> class buffer {
/** Returns a pointer to the buffer data. */
const T* data() const FMT_NOEXCEPT { return ptr_; }
/**
Resizes the buffer. If T is a POD type new elements may not be initialized.
*/
void resize(size_t new_size) {
reserve(new_size);
size_ = new_size;
}
/** Clears this buffer. */
void clear() { size_ = 0; }
/** Reserves space to store at least *capacity* elements. */
void reserve(size_t new_capacity) {
// Tries resizing the buffer to contain *count* elements. If T is a POD type
// the new elements may not be initialized.
void try_resize(size_t count) {
try_reserve(count);
size_ = count <= capacity_ ? count : capacity_;
}
// Tries increasing the buffer capacity to *new_capacity*. It can increase the
// capacity by a smaller amount than requested but guarantees there is space
// for at least one additional element either by increasing the capacity or by
// flushing the buffer if it is full.
void try_reserve(size_t new_capacity) {
if (new_capacity > capacity_) grow(new_capacity);
}
void push_back(const T& value) {
reserve(size_ + 1);
try_reserve(size_ + 1);
ptr_[size_++] = value;
}
@@ -705,32 +746,150 @@ template <typename T> class buffer {
}
};
// A container-backed buffer.
struct buffer_traits {
explicit buffer_traits(size_t) {}
size_t count() const { return 0; }
size_t limit(size_t size) { return size; }
};
class fixed_buffer_traits {
private:
size_t count_ = 0;
size_t limit_;
public:
explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
size_t count() const { return count_; }
size_t limit(size_t size) {
size_t n = limit_ > count_ ? limit_ - count_ : 0;
count_ += size;
return size < n ? size : n;
}
};
// A buffer that writes to an output iterator when flushed.
template <typename OutputIt, typename T, typename Traits = buffer_traits>
class iterator_buffer final : public Traits, public buffer<T> {
private:
OutputIt out_;
enum { buffer_size = 256 };
T data_[buffer_size];
protected:
void grow(size_t) final FMT_OVERRIDE {
if (this->size() == buffer_size) flush();
}
void flush();
public:
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
: Traits(n),
buffer<T>(data_, 0, buffer_size),
out_(out) {}
~iterator_buffer() { flush(); }
OutputIt out() {
flush();
return out_;
}
size_t count() const { return Traits::count() + this->size(); }
};
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
protected:
void grow(size_t) final FMT_OVERRIDE {}
public:
explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
T* out() { return &*this->end(); }
};
// A buffer that writes to a container with the contiguous storage.
template <typename Container>
class container_buffer : public buffer<typename Container::value_type> {
class iterator_buffer<std::back_insert_iterator<Container>,
enable_if_t<is_contiguous<Container>::value,
typename Container::value_type>>
final : public buffer<typename Container::value_type> {
private:
Container& container_;
protected:
void grow(size_t capacity) FMT_OVERRIDE {
void grow(size_t capacity) final FMT_OVERRIDE {
container_.resize(capacity);
this->set(&container_[0], capacity);
}
public:
explicit container_buffer(Container& c)
explicit iterator_buffer(Container& c)
: buffer<typename Container::value_type>(c.size()), container_(c) {}
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
: iterator_buffer(get_container(out)) {}
std::back_insert_iterator<Container> out() {
return std::back_inserter(container_);
}
};
// Extracts a reference to the container from back_insert_iterator.
template <typename Container>
inline Container& get_container(std::back_insert_iterator<Container> it) {
using bi_iterator = std::back_insert_iterator<Container>;
struct accessor : bi_iterator {
accessor(bi_iterator iter) : bi_iterator(iter) {}
using bi_iterator::container;
};
return *accessor(it).container;
// A buffer that counts the number of code units written discarding the output.
template <typename T = char> class counting_buffer final : public buffer<T> {
private:
enum { buffer_size = 256 };
T data_[buffer_size];
size_t count_ = 0;
protected:
void grow(size_t) final FMT_OVERRIDE {
if (this->size() != buffer_size) return;
count_ += this->size();
this->clear();
}
public:
counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
size_t count() { return count_ + this->size(); }
};
// An output iterator that appends to the buffer.
// It is used to reduce symbol sizes for the common case.
template <typename T>
class buffer_appender : public std::back_insert_iterator<buffer<T>> {
using base = std::back_insert_iterator<buffer<T>>;
public:
explicit buffer_appender(buffer<T>& buf) : base(buf) {}
buffer_appender(base it) : base(it) {}
buffer_appender& operator++() {
base::operator++();
return *this;
}
buffer_appender operator++(int) {
buffer_appender tmp = *this;
++*this;
return tmp;
}
};
// Maps an output iterator into a buffer.
template <typename T, typename OutputIt>
iterator_buffer<OutputIt, T> get_buffer(OutputIt);
template <typename T> buffer<T>& get_buffer(buffer_appender<T>);
template <typename OutputIt> OutputIt get_buffer_init(OutputIt out) {
return out;
}
template <typename T> buffer<T>& get_buffer_init(buffer_appender<T> out) {
return get_container(out);
}
template <typename Buffer>
auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
return buf.out();
}
template <typename T> buffer_appender<T> get_iterator(buffer<T>& buf) {
return buffer_appender<T>(buf);
}
template <typename T, typename Char = char, typename Enable = void>
@@ -759,7 +918,8 @@ template <typename Char> struct named_arg_info {
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
struct arg_data {
// args_[0].named_args points to named_args_ to avoid bloating format_args.
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : 1)];
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
template <typename... U>
@@ -771,7 +931,8 @@ struct arg_data {
template <typename T, typename Char, size_t NUM_ARGS>
struct arg_data<T, Char, NUM_ARGS, 0> {
T args_[NUM_ARGS != 0 ? NUM_ARGS : 1];
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
template <typename... U>
FMT_INLINE arg_data(const U&... init) : args_{init...} {}
@@ -959,6 +1120,8 @@ enum { long_short = sizeof(long) == sizeof(int) };
using long_type = conditional_t<long_short, int, long long>;
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
struct unformattable {};
// Maps formatting arguments to core types.
template <typename Context> struct arg_mapper {
using char_type = typename Context::char_type;
@@ -1067,15 +1230,7 @@ template <typename Context> struct arg_mapper {
return map(val.value);
}
int map(...) {
constexpr bool formattable = sizeof(Context) == 0;
static_assert(
formattable,
"Cannot format argument. To make type T formattable provide a "
"formatter<T> specialization: "
"https://fmt.dev/latest/api.html#formatting-user-defined-types");
return 0;
}
unformattable map(...) { return {}; }
};
// A type constant after applying arg_mapper<Context>.
@@ -1199,15 +1354,25 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
return vis(monostate());
}
// Checks whether T is a container with contiguous storage.
template <typename T> struct is_contiguous : std::false_type {};
template <typename Char>
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
template <typename Char>
struct is_contiguous<detail::buffer<Char>> : std::true_type {};
template <typename T> struct formattable : std::false_type {};
namespace detail {
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
template <typename... Ts>
using void_t = typename detail::void_t_impl<Ts...>::type;
template <typename It, typename T, typename Enable = void>
struct is_output_iterator : std::false_type {};
template <typename It, typename T>
struct is_output_iterator<
It, T,
void_t<typename std::iterator_traits<It>::iterator_category,
decltype(*std::declval<It>() = std::declval<T>())>>
: std::true_type {};
template <typename OutputIt>
struct is_back_insert_iterator : std::false_type {};
template <typename Container>
@@ -1219,6 +1384,9 @@ struct is_contiguous_back_insert_iterator : std::false_type {};
template <typename Container>
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
: is_contiguous<Container> {};
template <typename Char>
struct is_contiguous_back_insert_iterator<buffer_appender<Char>>
: std::true_type {};
// A type-erased reference to an std::locale to avoid heavy <locale> include.
class locale_ref {
@@ -1250,13 +1418,24 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
return arg;
}
template <typename T> int check(unformattable) {
static_assert(
formattable<T>(),
"Cannot format an argument. To make type T formattable provide a "
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
return 0;
}
template <typename T, typename U> inline const U& check(const U& val) {
return val;
}
// The type template parameter is there to avoid an ODR violation when using
// a fallback formatter in one translation unit and an implicit conversion in
// another (not recommended).
template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)>
inline value<Context> make_arg(const T& val) {
return arg_mapper<Context>().map(val);
return check<T>(arg_mapper<Context>().map(val));
}
template <bool IS_PACKED, typename Context, type, typename T,
@@ -1356,13 +1535,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
template <typename Char>
using buffer_context =
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
basic_format_context<detail::buffer_appender<Char>, Char>;
using format_context = buffer_context<char>;
using wformat_context = buffer_context<wchar_t>;
// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164.
// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
#define FMT_BUFFER_CONTEXT(Char) \
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>
basic_format_context<detail::buffer_appender<Char>, Char>
/**
\rst
@@ -1414,7 +1593,7 @@ class format_arg_store
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
Constructs a `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
can be omitted in which case it defaults to `~fmt::context`.
See `~fmt::arg` for lifetime considerations.
@@ -1426,6 +1605,27 @@ inline format_arg_store<Context, Args...> make_format_args(
return {args...};
}
/**
\rst
Constructs a `~fmt::format_arg_store` object that contains references
to arguments and can be implicitly converted to `~fmt::format_args`.
If ``format_str`` is a compile-time string then `make_args_checked` checks
its validity at compile time.
\endrst
*/
template <typename... Args, typename S, typename Char = char_t<S>>
inline auto make_args_checked(const S& format_str,
const remove_reference_t<Args>&... args)
-> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> {
static_assert(
detail::count<(
std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
std::is_reference<Args>::value)...>() == 0,
"passing views as lvalues is disallowed");
detail::check_format_string<Args...>(format_str);
return {args...};
}
/**
\rst
Returns a named argument to be used in a formatting function. It should only
@@ -1713,7 +1913,7 @@ template <typename Context> class basic_format_args {
}
template <typename Char> int get_id(basic_string_view<Char> name) const {
if (!has_named_args()) return {};
if (!has_named_args()) return -1;
const auto& named_args =
(is_packed() ? values_[-1] : args_[-1].value_).named_args;
for (size_t i = 0; i < named_args.size; ++i) {
@@ -1729,7 +1929,14 @@ template <typename Context> class basic_format_args {
}
};
/** An alias to ``basic_format_args<context>``. */
#ifdef FMT_ARM_ABI_COMPATIBILITY
/** An alias to ``basic_format_args<format_context>``. */
// Separate types would result in shorter symbols but break ABI compatibility
// between clang and gcc on ARM (#1919).
using format_args = basic_format_args<format_context>;
using wformat_args = basic_format_args<wformat_context>;
#else
// DEPRECATED! These are kept for ABI compatibility.
// It is a separate type rather than an alias to make symbols readable.
struct format_args : basic_format_args<format_context> {
template <typename... Args>
@@ -1738,32 +1945,10 @@ struct format_args : basic_format_args<format_context> {
struct wformat_args : basic_format_args<wformat_context> {
using basic_format_args::basic_format_args;
};
#endif
namespace detail {
// Reports a compile-time error if S is not a valid format string.
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
FMT_INLINE void check_format_string(const S&) {
#ifdef FMT_ENFORCE_COMPILE_STRING
static_assert(is_compile_string<S>::value,
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
"FMT_STRING.");
#endif
}
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
template <typename... Args, typename S, typename Char = char_t<S>>
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
make_args_checked(const S& format_str,
const remove_reference_t<Args>&... args) {
static_assert(count<(std::is_base_of<view, remove_reference_t<Args>>::value &&
std::is_reference<Args>::value)...>() == 0,
"passing views as lvalues is disallowed");
check_format_string<Args...>(format_str);
return {args...};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
std::basic_string<Char> vformat(
basic_string_view<Char> format_str,
@@ -1772,9 +1957,10 @@ std::basic_string<Char> vformat(
FMT_API std::string vformat(string_view format_str, format_args args);
template <typename Char>
typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
void vformat_to(
buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args);
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
detail::locale_ref loc = {});
template <typename Char, typename Args,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
@@ -1789,26 +1975,80 @@ inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
/** Formats a string and writes the output to ``out``. */
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
template <
typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
OutputIt vformat_to(
OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto& c = detail::get_container(out);
detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
template <typename OutputIt, typename S, typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value>
auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> typename std::enable_if<enable, OutputIt>::type {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
detail::vformat_to(buf, to_string_view(format_str), args);
return out;
return detail::get_iterator(buf);
}
template <typename Container, typename S, typename... Args,
FMT_ENABLE_IF(
is_contiguous<Container>::value&& detail::is_string<S>::value)>
inline std::back_insert_iterator<Container> format_to(
std::back_insert_iterator<Container> out, const S& format_str,
Args&&... args) {
return vformat_to(out, to_string_view(format_str),
detail::make_args_checked<Args...>(format_str, args...));
/**
\rst
Formats arguments, writes the result to the output iterator ``out`` and returns
the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out), "{}", 42);
\endrst
*/
// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3.
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const S& format_str, Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, to_string_view(format_str), vargs);
}
template <typename OutputIt> struct format_to_n_result {
/** Iterator past the end of the output range. */
OutputIt out;
/** Total (not truncated) output size. */
size_t size;
};
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline format_to_n_result<OutputIt> vformat_to_n(
OutputIt out, size_t n, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
/**
\rst
Formats arguments, writes up to ``n`` characters of the result to the output
iterator ``out`` and returns the total output size and the iterator past the
end of the output range.
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to_n(OutputIt out, size_t n, const S& format_str,
const Args&... args) ->
typename std::enable_if<enable, format_to_n_result<OutputIt>>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to_n(out, n, to_string_view(format_str), vargs);
}
/**
Returns the number of characters in the output of
``format(format_str, args...)``.
*/
template <typename... Args>
inline size_t formatted_size(string_view format_str, Args&&... args) {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
detail::counting_buffer<> buf;
detail::vformat_to(buf, format_str, vargs);
return buf.count();
}
template <typename S, typename Char = char_t<S>>
@@ -1832,7 +2072,7 @@ FMT_INLINE std::basic_string<Char> vformat(
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>>
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return detail::vformat(to_string_view(format_str), vargs);
}
@@ -1852,7 +2092,7 @@ FMT_API void vprint(std::FILE*, string_view, format_args);
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return detail::is_unicode<Char>()
? vprint(f, to_string_view(format_str), vargs)
: detail::vprint_mojibake(f, to_string_view(format_str), vargs);
@@ -1871,7 +2111,7 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(const S& format_str, Args&&... args) {
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return detail::is_unicode<Char>()
? vprint(to_string_view(format_str), vargs)
: detail::vprint_mojibake(stdout, to_string_view(format_str),

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -15,22 +15,12 @@
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
args, detail::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(loc, buffer, format_str, args);
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer);
}
} // namespace detail
@@ -45,32 +35,28 @@ inline std::basic_string<Char> vformat(
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return detail::vformat(
loc, to_string_view(format_str),
detail::make_args_checked<Args...>(format_str, args...));
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
format_args_t<type_identity_t<OutputIt>, Char> args) {
using af = detail::arg_formatter<OutputIt, Char>;
return vformat_to<af>(out, to_string_view(format_str), args,
detail::locale_ref(loc));
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
detail::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
detail::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
FMT_END_NAMESPACE

View File

@@ -29,7 +29,8 @@
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
@@ -278,7 +279,8 @@ class file {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT) // Create if the file doesn't exist.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
};
// Constructs a file object which doesn't represent any file.
@@ -343,36 +345,69 @@ class file {
// Returns the memory page size.
long getpagesize();
class direct_buffered_file;
namespace detail {
template <typename S, typename... Args>
void print(direct_buffered_file& f, const S& format_str,
const Args&... args);
struct buffer_size {
size_t value = 0;
buffer_size operator=(size_t val) const {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
// A buffered file with a direct buffer access and no synchronization.
class direct_buffered_file {
struct ostream_params {
int oflag = file::WRONLY | file::CREATE;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
template <typename... T>
ostream_params(T... params, int oflag) : ostream_params(params...) {
this->oflag = oflag;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
};
} // namespace detail
static constexpr detail::buffer_size buffer_size;
// A fast output stream which is not thread-safe.
class ostream final : private detail::buffer<char> {
private:
file file_;
enum { buffer_size = 4096 };
char buffer_[buffer_size];
int pos_;
void flush() {
if (pos_ == 0) return;
file_.write(buffer_, pos_);
pos_ = 0;
if (size() == 0) return;
file_.write(data(), size());
clear();
}
int free_capacity() const { return buffer_size - pos_; }
FMT_API void grow(size_t) override final;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public:
direct_buffered_file(cstring_view path, int oflag)
: file_(path, oflag), pos_(0) {}
~direct_buffered_file() {
flush();
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
@@ -380,25 +415,20 @@ class direct_buffered_file {
}
template <typename S, typename... Args>
friend void print(direct_buffered_file& f, const S& format_str,
const Args&... args) {
// We could avoid double buffering.
auto buf = fmt::memory_buffer();
fmt::format_to(std::back_inserter(buf), format_str, args...);
auto remaining_pos = 0;
auto remaining_size = buf.size();
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
auto size = f.free_capacity();
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
f.pos_ += size;
f.flush();
remaining_pos += size;
remaining_size -= size;
}
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
f.pos_ += static_cast<int>(remaining_size);
void print(const S& format_str, const Args&... args) {
format_to(detail::buffer_appender<char>(*this), format_str, args...);
}
};
/**
Opens a file for writing. Supported parameters passed in `params`:
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE

View File

@@ -49,17 +49,27 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
}
};
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
// Hide all operator<< from std::basic_ostream<Char>.
void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
@@ -103,7 +113,7 @@ void format_value(buffer<Char>& buf, const T& value,
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.resize(buf.size());
buf.try_resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
@@ -160,7 +170,7 @@ template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
detail::make_args_checked<Args...>(format_str, args...));
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -181,7 +181,7 @@ template <typename Char> class printf_width_handler {
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
Context(buffer_appender<Char>(buf), format, args).format();
}
} // namespace detail
@@ -598,7 +598,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;

View File

@@ -157,6 +157,9 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
@@ -182,7 +185,6 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace detail
template <typename T> struct is_tuple_like {
@@ -246,9 +248,18 @@ template <typename T, typename Char> struct is_range {
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
struct formatter<RangeT, Char,
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&&
(has_formatter<detail::value_type<T>, format_context>::value ||
detail::has_fallback_formatter<detail::value_type<T>,
format_context>::value)
#endif
>> {
formatting_range<Char> formatting;
template <typename ParseContext>
@@ -257,8 +268,7 @@ struct formatter<RangeT, Char,
}
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto it = values.begin();

View File

@@ -23,6 +23,36 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
FMT_NOEXCEPT;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
FMT_NOEXCEPT;
// DEPRECATED! This function exists for ABI compatibility.
template <typename Char>
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
Char>::iterator
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>>
args) {
using iterator = std::back_insert_iterator<buffer<char>>;
using context = basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>;
auto out = iterator(buf);
format_handler<iterator, Char, context> h(out, format_str, args, {});
parse_format_string<false>(format_str, h);
return out;
}
template basic_format_context<std::back_insert_iterator<buffer<char>>,
char>::iterator
vformat_to(buffer<char>&, string_view,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<char>>>,
type_identity_t<char>>>);
} // namespace detail
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
@@ -44,9 +74,9 @@ template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<char>::append(const char*, const char*);
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>);
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);

View File

@@ -62,7 +62,7 @@ using RWResult = int;
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using RWResult = ssize_t;
@@ -124,7 +124,8 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
format_to(std::back_inserter(out), "{}: {}", message, utf8_message);
format_to(buffer_appender<char>(out), "{}: {}", message,
utf8_message);
return;
}
break;
@@ -288,12 +289,12 @@ void file::pipe(file& read_end, file& write_end) {
}
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
#if defined(__MINGW32__) && defined(_POSIX_)
// Don't retry as fdopen doesn't return EINTR.
# if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
#else
# else
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
#endif
# endif
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
@@ -313,5 +314,9 @@ long getpagesize() {
return size;
# endif
}
FMT_API void ostream::grow(size_t) {
if (this->size() == this->capacity()) flush();
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

1745
patch.diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -17,5 +17,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
### Fonts Included
* Cascadia Code, Cascadia Mono (2102.03)
* from microsoft/cascadia-code@b358d1ba3d1629c113671312b18eab52797cc055
* Cascadia Code, Cascadia Mono (2102.25)
* from microsoft/cascadia-code@911dc421f333e3b72b97381d16fee5b71eb48f04

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -43,6 +43,7 @@
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Server" Name="1A541C01-589A-496E-85A7-A9E02170166D"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser" Name="c9ba2a84-d3ca-5e19-2bd6-776a0910cb9d"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Render.VtEngine" Name="c9ba2a95-d3ca-5e19-2bd6-776a0910cb9d"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.UIA" Name="e7ebce59-2161-572d-b263-2f16a6afb9e5"/>
<!-- Now define some profiles. We'll call them by ID when collecting. Also, the Base is where it is inheriting from and is a .wprpi file built... -->
<!-- ... into WPR automatically. Go look in the WPR install directory or in the documentation to find it. -->
<Profile Id="ConsolePerfProfile.Verbose.File" Base="GeneralProfile.Light.File" LoggingMode="File" Name="ConsolePerfProfile" DetailLevel="Verbose" Description="Console Performance default profile">
@@ -66,6 +67,7 @@
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Server"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Render.VtEngine"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.UIA"/>
</EventProviders>
</EventCollectorId>
</Collectors>

View File

@@ -281,9 +281,9 @@ bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribut
// - false otherwise (out of memory)
bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute)
{
// Assert the buffer state is ready for this character
// This function corrects most errors. If this is false, we had an uncorrectable one.
FAIL_FAST_IF(!(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
// This function corrects most errors. If this is false, we had an uncorrectable one which
// older versions of conhost simply let pass by unflinching.
LOG_HR_IF(E_NOT_VALID_STATE, !(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
bool fSuccess = true;
// Now compensate if we don't have enough space for the upcoming double byte sequence

View File

@@ -54,7 +54,6 @@
<uap:ShowOn Tile="square310x310Logo"/>
</uap:ShowNameOnTiles>
</uap:DefaultTile>
<uap:SplashScreen Image="Images\SplashScreen.png"/>
</uap:VisualElements>
<Extensions>
@@ -78,17 +77,17 @@
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
<com:Class Id="9f156763-7844-4dc4-b2b1-901f640f5155" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
<com:Class Id="52065414-e077-47ec-a3ac-1cc5455e1b54" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
</com:SurrogateServer>
</com:ComServer>
</com:Extension>
<desktop4:Extension Category="windows.fileExplorerContextMenus">
<desktop4:FileExplorerContextMenus>
<desktop5:ItemType Type="Directory">
<desktop5:Verb Id="Command1" Clsid="9f156763-7844-4dc4-b2b1-901f640f5155" />
<desktop5:Verb Id="OpenTerminalDev" Clsid="52065414-e077-47ec-a3ac-1cc5455e1b54" />
</desktop5:ItemType>
<desktop5:ItemType Type="Directory\Background">
<desktop5:Verb Id="Command2" Clsid="9f156763-7844-4dc4-b2b1-901f640f5155" />
<desktop5:Verb Id="OpenTerminalDev" Clsid="52065414-e077-47ec-a3ac-1cc5455e1b54" />
</desktop5:ItemType>
</desktop4:FileExplorerContextMenus>
</desktop4:Extension>

View File

@@ -55,7 +55,6 @@
<uap:ShowOn Tile="square310x310Logo"/>
</uap:ShowNameOnTiles>
</uap:DefaultTile>
<uap:SplashScreen Image="Images\SplashScreen.png"/>
</uap:VisualElements>
<Extensions>
@@ -78,17 +77,17 @@
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
<com:Class Id="9f156763-7844-4dc4-b2b1-901f640f5155" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
<com:Class Id="02db545a-3e20-46de-83a5-1329b1e88b6b" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
</com:SurrogateServer>
</com:ComServer>
</com:Extension>
<desktop4:Extension Category="windows.fileExplorerContextMenus">
<desktop4:FileExplorerContextMenus>
<desktop5:ItemType Type="Directory">
<desktop5:Verb Id="Command1" Clsid="9f156763-7844-4dc4-b2b1-901f640f5155" />
<desktop5:Verb Id="OpenTerminalPre" Clsid="02db545a-3e20-46de-83a5-1329b1e88b6b" />
</desktop5:ItemType>
<desktop5:ItemType Type="Directory\Background">
<desktop5:Verb Id="Command2" Clsid="9f156763-7844-4dc4-b2b1-901f640f5155" />
<desktop5:Verb Id="OpenTerminalPre" Clsid="02db545a-3e20-46de-83a5-1329b1e88b6b" />
</desktop5:ItemType>
</desktop4:FileExplorerContextMenus>
</desktop4:Extension>

View File

@@ -55,7 +55,6 @@
<uap:ShowOn Tile="square310x310Logo"/>
</uap:ShowNameOnTiles>
</uap:DefaultTile>
<uap:SplashScreen Image="Images\SplashScreen.png"/>
</uap:VisualElements>
<Extensions>
@@ -86,10 +85,10 @@
<desktop4:Extension Category="windows.fileExplorerContextMenus">
<desktop4:FileExplorerContextMenus>
<desktop5:ItemType Type="Directory">
<desktop5:Verb Id="Command1" Clsid="9f156763-7844-4dc4-b2b1-901f640f5155" />
<desktop5:Verb Id="OpenTerminalHere" Clsid="9f156763-7844-4dc4-b2b1-901f640f5155" />
</desktop5:ItemType>
<desktop5:ItemType Type="Directory\Background">
<desktop5:Verb Id="Command2" Clsid="9f156763-7844-4dc4-b2b1-901f640f5155" />
<desktop5:Verb Id="OpenTerminalHere" Clsid="9f156763-7844-4dc4-b2b1-901f640f5155" />
</desktop5:ItemType>
</desktop4:FileExplorerContextMenus>
</desktop4:Extension>

View File

@@ -79,6 +79,7 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestCommandsAndKeybindings);
TEST_METHOD(TestNestedCommandWithoutName);
TEST_METHOD(TestNestedCommandWithBadSubCommands);
TEST_METHOD(TestUnbindNestedCommand);
TEST_METHOD(TestRebindNestedCommand);
@@ -1376,7 +1377,7 @@ namespace SettingsModelLocalTests
},
{
"name": "parent",
"commands": [
"commands": [
{ "command": { "action": "setColorScheme", "colorScheme": "invalidScheme" } }
]
}
@@ -1403,11 +1404,11 @@ namespace SettingsModelLocalTests
},
{
"name": "grandparent",
"commands": [
"commands": [
{
"name": "parent",
"commands": [
{
{
"command": { "action": "setColorScheme", "colorScheme": "invalidScheme" }
}
]
@@ -1909,7 +1910,8 @@ namespace SettingsModelLocalTests
"keybindings": [
{ "command": { "action": "splitPane", "split":"auto" }, "keys": [ "ctrl+alt+t", "ctrl+a" ] },
{ "command": { "action": "moveFocus" }, "keys": [ "ctrl+a" ] },
{ "command": { "action": "resizePane" }, "keys": [ "ctrl+b" ] }
{ "command": { "action": "resizePane" }, "keys": [ "ctrl+b" ] },
{ "name": "invalid nested", "commands":[ { "name" : "hello" }, { "name" : "world" } ] }
]
})" };
@@ -1918,18 +1920,20 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(0u, settings->_globals->_keymap->_keyShortcuts.size());
VERIFY_ARE_EQUAL(3u, settings->_globals->_keybindingsWarnings.size());
VERIFY_ARE_EQUAL(4u, settings->_globals->_keybindingsWarnings.size());
VERIFY_ARE_EQUAL(SettingsLoadWarnings::TooManyKeysForChord, settings->_globals->_keybindingsWarnings.at(0));
VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(1));
VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_globals->_keybindingsWarnings.at(2));
VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->_globals->_keybindingsWarnings.at(3));
settings->_ValidateKeybindings();
VERIFY_ARE_EQUAL(4u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(5u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(SettingsLoadWarnings::TooManyKeysForChord, settings->_warnings.GetAt(1));
VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(2));
VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.GetAt(3));
VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->_warnings.GetAt(4));
}
void DeserializationTests::ValidateExecuteCommandlineWarning()
@@ -2316,6 +2320,53 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(0u, commands.Size());
}
void DeserializationTests::TestNestedCommandWithBadSubCommands()
{
// This test tests a nested command without a name specified. This type
// of command should just be ignored, since we can't auto-generate names
// for nested commands, they _must_ have names specified.
const std::string settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": [
{
"name": "profile0",
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"historySize": 1,
"commandline": "cmd.exe"
}
],
"actions": [
{
"name": "nested command",
"commands": [
{
"name": "child1"
},
{
"name": "child2"
}
]
},
],
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
})" };
VerifyParseSucceeded(settingsJson);
auto settings = winrt::make_self<implementation::CascadiaSettings>();
settings->_ParseJsonString(settingsJson, false);
settings->LayerJson(settings->_userSettings);
auto commands = settings->_globals->Commands();
settings->_ValidateSettings();
VERIFY_ARE_EQUAL(2u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.GetAt(0));
VERIFY_ARE_EQUAL(SettingsLoadWarnings::FailedToParseSubCommands, settings->_warnings.GetAt(1));
VERIFY_ARE_EQUAL(0u, commands.Size());
}
void DeserializationTests::TestUnbindNestedCommand()
{
// Test that layering a command with `"commands": null` set will unbind a command that already exists.

View File

@@ -1595,129 +1595,58 @@ namespace TerminalAppLocalTests
{
{
std::vector<winrt::hstring> args{ L"wt.exe" };
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"-1" };
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"new" };
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"-12345" };
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"12345" };
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(12345, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(12345, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(12345, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"0" };
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"last" };
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"new-tab" };
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
}
{
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"new-tab" };
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId());
VERIFY_ARE_EQUAL(L"new-tab", result.WindowName());
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId());
VERIFY_ARE_EQUAL(L"new-tab", result.WindowName());
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId());
VERIFY_ARE_EQUAL(L"new-tab", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
}
}
@@ -1728,23 +1657,19 @@ namespace TerminalAppLocalTests
// This is a little helper to make sure that these args _always_ return
// UseNew, regardless of the windowing behavior.
auto testHelper = [](auto&& args) {
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
};
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"--help" });
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"new-tab", L"--help" });
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"-w", L"0", L"new-tab", L"--help" });
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"-w", L"foo", L"new-tab", L"--help" });
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"new-tab", L";", L"--help" });
}
@@ -1755,17 +1680,14 @@ namespace TerminalAppLocalTests
// This is a little helper to make sure that these args _always_ return
// UseNew, regardless of the windowing behavior.
auto testHelper = [](auto&& args) {
auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting);
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId());
VERIFY_ARE_EQUAL(L"", result.WindowName());
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
};
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"--version" });

View File

@@ -27,7 +27,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
GETSET_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr);
GETSET_PROPERTY(int, ResultTargetWindow, -1);
GETSET_PROPERTY(winrt::hstring, ResultTargetWindowName, L"");
public:
FindTargetWindowArgs(winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) :

View File

@@ -25,9 +25,6 @@
<ClInclude Include="ProposeCommandlineResult.h">
<DependentUpon>Monarch.idl</DependentUpon>
</ClInclude>
<ClInclude Include="RenameRequestArgs.h">
<DependentUpon>Peasant.idl</DependentUpon>
</ClInclude>
<ClInclude Include="WindowActivatedArgs.h">
<DependentUpon>Peasant.idl</DependentUpon>
</ClInclude>
@@ -54,9 +51,6 @@
<ClCompile Include="ProposeCommandlineResult.cpp">
<DependentUpon>Monarch.idl</DependentUpon>
</ClCompile>
<ClCompile Include="RenameRequestArgs.cpp">
<DependentUpon>Peasant.idl</DependentUpon>
</ClCompile>
<ClCompile Include="WindowActivatedArgs.cpp">
<DependentUpon>Peasant.idl</DependentUpon>
</ClCompile>

View File

@@ -75,8 +75,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
auto newPeasantsId = peasant.GetID();
// Add an event listener to the peasant's WindowActivated event.
peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated });
peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows });
peasant.RenameRequested({ this, &Monarch::_renameRequested });
_peasants[newPeasantsId] = peasant;
@@ -149,56 +147,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
}
uint64_t Monarch::_lookupPeasantIdForName(const winrt::hstring& name)
{
if (name.empty())
{
return 0;
}
std::vector<uint64_t> peasantsToErase{};
uint64_t result = 0;
for (const auto& [id, p] : _peasants)
{
try
{
auto otherName = p.WindowName();
if (otherName == name)
{
result = id;
break;
}
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
// Normally, we'd just erase the peasant here. However, we can't
// erase from the map while we're iterating over it like this.
// Instead, pull a good ole Java and collect this id for removal
// later.
peasantsToErase.push_back(id);
// // Remove the peasant from the list of peasants
// _peasants.erase(id);
// // Remove the peasant from the list of MRU windows. They're dead.
// // They can't be the MRU anymore.
// _clearOldMruEntries(id);
}
}
// Remove the dead peasants we came across while iterating.
for (const auto& id : peasantsToErase)
{
// Remove the peasant from the list of peasants
_peasants.erase(id);
// Remove the peasant from the list of MRU windows. They're dead.
// They can't be the MRU anymore.
_clearOldMruEntries(id);
}
return result;
}
// Method Description:
// - Handler for the `Peasant::WindowActivated` event. We'll make a in-proc
// copy of the WindowActivatedArgs from the peasant. That way, we won't
@@ -452,7 +400,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// After the event was handled, ResultTargetWindow() will be filled with
// the parsed result.
const auto targetWindow = findWindowArgs->ResultTargetWindow();
const auto& targetWindowName = findWindowArgs->ResultTargetWindowName();
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_ProposeCommandline",
@@ -463,7 +410,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// that goes with it. Alternatively, if we were given a magic windowing
// constant, we can use that to look up an appropriate peasant.
if (targetWindow >= 0 ||
targetWindow == WindowingBehaviorUseName ||
targetWindow == WindowingBehaviorUseExisting ||
targetWindow == WindowingBehaviorUseAnyExisting)
{
@@ -483,9 +429,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
case WindowingBehaviorUseAnyExisting:
windowID = _getMostRecentPeasantID(false);
break;
case WindowingBehaviorUseName:
windowID = _lookupPeasantIdForName(targetWindowName);
break;
default:
windowID = ::base::saturated_cast<uint64_t>(targetWindow);
break;
@@ -506,7 +449,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
if (auto targetPeasant{ _getPeasant(windowID) })
{
auto result{ winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(false) };
result->WindowName(targetWindowName);
try
{
// This will raise the peasant's ExecuteCommandlineRequested
@@ -554,7 +497,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
auto result{ winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true) };
result->Id(windowID);
result->WindowName(targetWindowName);
return *result;
}
}
@@ -566,69 +508,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
// In this case, no usable ID was provided. Return { true, nullopt }
auto result = winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true);
result->WindowName(targetWindowName);
return *result;
}
void Monarch::_identifyWindows(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
// Notify all the peasants to display their ID.
for (const auto& [id, p] : _peasants)
{
try
{
p.DisplayWindowId();
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
// If this fails, we don't _really_ care. Just move on to the
// next one. Someone else will clean up the dead peasant.
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_identifyWindows_Failed",
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
}
}
void Monarch::_renameRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args)
{
bool successfullyRenamed = false;
try
{
args.Succeeded(false);
auto name{ args.NewName() };
// Try to find a peasant that currently has this name
auto id = _lookupPeasantIdForName(name);
if (auto p{ _getPeasant(id) })
{
// If there is one, then oh no! The requestor is not allowed to
// be renamed.
}
else
{
args.Succeeded(true);
successfullyRenamed = true;
}
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_renameRequested",
TraceLoggingWideString(name.c_str(), "name", "The newly proposed name"),
TraceLoggingInt64(successfullyRenamed, "successfullyRenamed", "true if the peasant is allowed to rename themselves to that name."),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
// If this fails, we don't _really_ care. The peasant died, but
// they're the only one who cares about the result.
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_renameRequested_Failed",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
return winrt::make<Remoting::implementation::ProposeCommandlineResult>(true);
}
}

View File

@@ -67,19 +67,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID);
uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop);
uint64_t _lookupPeasantIdForName(const winrt::hstring& name);
void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
void _doHandleActivatePeasant(const winrt::com_ptr<winrt::Microsoft::Terminal::Remoting::implementation::WindowActivatedArgs>& args);
void _clearOldMruEntries(const uint64_t peasantID);
void _identifyWindows(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
friend class RemotingUnitTests::RemotingTests;
};
}

View File

@@ -9,12 +9,11 @@ namespace Microsoft.Terminal.Remoting
[default_interface] runtimeclass FindTargetWindowArgs {
CommandlineArgs Args { get; };
Int32 ResultTargetWindow;
String ResultTargetWindowName;
}
[default_interface] runtimeclass ProposeCommandlineResult {
Windows.Foundation.IReference<UInt64> Id { get; };
String WindowName { get; };
// TODO:projects/5 - also return the name here, if the name was set on the commandline
Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode
}

View File

@@ -117,65 +117,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return _lastActivatedArgs;
}
void Peasant::DisplayWindowId()
{
// Not worried about try/catching this. The handler is in AppHost, which
// is in-proc for us.
_DisplayWindowIdRequestedHandlers(*this, nullptr);
}
void Peasant::RequestIdentifyWindows()
{
bool successfullyNotified = false;
try
{
// Try/catch this, because the other side of this event is handled
// by the monarch. The monarch might have died. If they have, this
// will throw an exception. Just eat it, the election thread will
// handle hooking up the new one.
_IdentifyWindowsRequestedHandlers(*this, nullptr);
successfullyNotified = true;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestIdentifyWindows",
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
void Peasant::RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args)
{
bool successfullyNotified = false;
auto oldName{ _WindowName };
try
{
// Try/catch this, because the other side of this event is handled
// by the monarch. The monarch might have died. If they have, this
// will throw an exception. Just eat it, the election thread will
// handle hooking up the new one.
_RenameRequestedHandlers(*this, args);
if (args.Succeeded())
{
_WindowName = args.NewName();
}
successfullyNotified = true;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestRename",
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
TraceLoggingWideString(oldName.c_str(), "oldName", "Our old name"),
TraceLoggingWideString(args.NewName().c_str(), "newName", "The proposed name"),
TraceLoggingBoolean(args.Succeeded(), "succeeded", "true if the monarch ok'd this new name for us."),
TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
}

View File

@@ -5,7 +5,6 @@
#include "Peasant.g.h"
#include "../cascadia/inc/cppwinrt_utils.h"
#include "RenameRequestArgs.h"
namespace RemotingUnitTests
{
@@ -23,20 +22,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
void ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
void RequestIdentifyWindows();
void DisplayWindowId();
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
GETSET_PROPERTY(winrt::hstring, WindowName, L"");
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs);
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs);
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
private:
Peasant(const uint64_t testPID);

View File

@@ -13,14 +13,6 @@ namespace Microsoft.Terminal.Remoting
String CurrentDirectory();
};
runtimeclass RenameRequestArgs
{
RenameRequestArgs(String newName);
String NewName { get; };
Boolean Succeeded;
};
runtimeclass WindowActivatedArgs
{
WindowActivatedArgs(UInt64 peasantID, Guid desktopID, Windows.Foundation.DateTime activatedTime);
@@ -41,17 +33,9 @@ namespace Microsoft.Terminal.Remoting
Boolean ExecuteCommandline(CommandlineArgs args);
void ActivateWindow(WindowActivatedArgs args);
WindowActivatedArgs GetLastActivatedArgs();
String WindowName { get; };
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
void DisplayWindowId(); // Tells us to display its own ID (which causes a DisplayWindowIdRequested to be raised)
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
};
[default_interface] runtimeclass Peasant : IPeasant

View File

@@ -27,7 +27,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
public:
GETSET_PROPERTY(Windows::Foundation::IReference<uint64_t>, Id);
GETSET_PROPERTY(winrt::hstring, WindowName, L"");
GETSET_PROPERTY(bool, ShouldCreateWindow, true);
public:

View File

@@ -1,5 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "RenameRequestArgs.h"
#include "RenameRequestArgs.g.cpp"

View File

@@ -1,30 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Class Name:
- RenameRequestArgs.h
--*/
#pragma once
#include "RenameRequestArgs.g.h"
#include "../cascadia/inc/cppwinrt_utils.h"
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct RenameRequestArgs : public RenameRequestArgsT<RenameRequestArgs>
{
GETSET_PROPERTY(winrt::hstring, NewName, L"");
GETSET_PROPERTY(bool, Succeeded, false);
public:
RenameRequestArgs(winrt::hstring newName) :
_NewName{ newName } {};
};
}
namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
{
BASIC_FACTORY(RenameRequestArgs);
}

View File

@@ -28,7 +28,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
struct WindowActivatedArgs : public WindowActivatedArgsT<WindowActivatedArgs>
{
GETSET_PROPERTY(uint64_t, PeasantID, 0);
GETSET_PROPERTY(winrt::guid, DesktopID, {});
GETSET_PROPERTY(winrt::guid, DesktopID);
GETSET_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {});
GETSET_PROPERTY(uint64_t, Hwnd, 0);

View File

@@ -5,8 +5,6 @@
#include "WindowManager.h"
#include "MonarchFactory.h"
#include "CommandlineArgs.h"
#include "../inc/WindowingBehavior.h"
#include "FindTargetWindowArgs.h"
#include "WindowManager.g.cpp"
#include "../../types/inc/utils.hpp"
@@ -71,7 +69,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// Otherwise, the King will tell us if we should make a new window
_shouldCreateWindow = _isKing;
std::optional<uint64_t> givenID;
winrt::hstring givenName = L""; // TODO:MG If we're the king, we might STILL WANT TO GET THE NAME. How do we get the name?
if (!_isKing)
{
// The monarch may respond back "you should be a new
@@ -89,7 +86,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
givenID = result.Id().Value();
}
givenName = result.WindowName();
// TraceLogging doesn't have a good solution for logging an
// optional. So we have to repeat the calls here:
if (givenID)
@@ -98,7 +95,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
"WindowManager_ProposeCommandline",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"),
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
else
@@ -107,7 +103,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
"WindowManager_ProposeCommandline",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingPointer(nullptr, "Id", "No ID provided"),
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
}
@@ -115,50 +110,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
// We're the monarch, we don't need to propose anything. We're just
// going to do it.
//
// However, we _do_ need to ask what our name should be. It's
// possible someone started the _first_ wt with something like `wt
// -w king` as the commandline - we want to make sure we set our
// name to "king".
auto findWindowArgs{ winrt::make_self<Remoting::implementation::FindTargetWindowArgs>(args) };
_raiseFindTargetWindowRequested(nullptr, *findWindowArgs);
auto responseId = findWindowArgs->ResultTargetWindow();
if (responseId > 0)
{
givenID = ::base::saturated_cast<uint64_t>(responseId);
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ProposeCommandline_AsMonarch",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"),
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
else if (responseId == WindowingBehaviorUseName)
{
givenName = findWindowArgs->ResultTargetWindowName();
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ProposeCommandline_AsMonarch",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
else
{
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ProposeCommandline_AsMonarch",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
TraceLoggingWrite(g_hRemotingProvider,
"WindowManager_ProposeCommandline_AsMonarch",
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
}
if (_shouldCreateWindow)
{
// If we should create a new window, then instantiate our Peasant
// instance, and tell that peasant to handle that commandline.
_createOurPeasant({ givenID }, givenName);
_createOurPeasant({ givenID });
// Spawn a thread to wait on the monarch, and handle the election
if (!_isKing)
@@ -246,17 +208,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return (ourPID == kingPID);
}
Remoting::IPeasant WindowManager::_createOurPeasant(std::optional<uint64_t> givenID,
const winrt::hstring& givenName)
Remoting::IPeasant WindowManager::_createOurPeasant(std::optional<uint64_t> givenID)
{
auto p = winrt::make_self<Remoting::implementation::Peasant>();
if (givenID)
{
p->AssignID(givenID.value());
}
// If the name wasn't specified, this will be an empty string.
p->WindowName(givenName);
_peasant = *p;
// Try to add us to the monarch. If that fails, try to find a monarch

View File

@@ -54,8 +54,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void _createMonarch();
void _createMonarchAndCallbacks();
bool _areWeTheKing();
winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional<uint64_t> givenID,
const winrt::hstring& givenName);
winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional<uint64_t> givenID);
bool _performElection();
void _createPeasantThread();

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.201017.1" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
</packages>

View File

@@ -56,7 +56,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
// Append a "\." to the given path, so that this will work in "C:\"
std::wstring cmdline = fmt::format(L"\"{}\" -d \"{}\\.\"", GetWtExePath(), pszName.get());
auto cmdline{ wil::str_printf<std::wstring>(LR"-("%s" -d "%s\.")-", GetWtExePath().c_str(), pszName.get()) };
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
nullptr,
cmdline.data(),

View File

@@ -27,8 +27,15 @@ Author(s):
using namespace Microsoft::WRL;
struct __declspec(uuid("9f156763-7844-4dc4-b2b1-901f640f5155"))
OpenTerminalHere : public RuntimeClass<RuntimeClassFlags<ClassicCom | InhibitFtmBase>, IExplorerCommand>
struct
#if defined(WT_BRANDING_RELEASE)
__declspec(uuid("9f156763-7844-4dc4-b2b1-901f640f5155"))
#elif defined(WT_BRANDING_PREVIEW)
__declspec(uuid("02db545a-3e20-46de-83a5-1329b1e88b6b"))
#else // DEV
__declspec(uuid("52065414-e077-47ec-a3ac-1cc5455e1b54"))
#endif
OpenTerminalHere : public RuntimeClass<RuntimeClassFlags<ClassicCom | InhibitFtmBase>, IExplorerCommand>
{
#pragma region IExplorerCommand
STDMETHODIMP Invoke(IShellItemArray* psiItemArray,

View File

@@ -1,10 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "PlaceholderType.h"
#include "PlaceholderType.g.cpp"
namespace winrt::Microsoft::Terminal::ShellExtension::implementation
{
}

View File

@@ -1,37 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- PlaceholderType.h
Abstract:
- This class is just here to make our .wapproj play nicely with this project. If
we don't define any winrt types, then we won't generate a .winmd, and the
.wapproj will become _very_ mad at this project. So we'll use this placeholder
class just to trick cppwinrt into generating a winmd for us. If we ever _do_
add a real winrt type to this project, this can be removed.
Author(s):
- Mike Griese - May 2020
--*/
#pragma once
#include <conattrs.hpp>
#include "PlaceholderType.g.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
namespace winrt::Microsoft::Terminal::ShellExtension::implementation
{
struct PlaceholderType : PlaceholderTypeT<PlaceholderType>
{
PlaceholderType() = default;
GETSET_PROPERTY(int32_t, Placeholder, 42);
};
}
namespace winrt::Microsoft::Terminal::ShellExtension::factory_implementation
{
BASIC_FACTORY(PlaceholderType);
}

View File

@@ -1,21 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// This class is just here to make our .wapproj play nicely with this project.
// If we don't define any winrt types, then we won't generate a .winmd, and the
// .wapproj will become _very_ mad at this project. So we'll use this
// placeholder class just to trick cppwinrt into generating a winmd for us. If
// we ever _do_ add a real winrt type to this project, this can be removed.
namespace Microsoft.Terminal.ShellExtension
{
[default_interface] runtimeclass PlaceholderType {
PlaceholderType();
Int32 Placeholder
{
get;
set;
};
};
}

View File

@@ -1,4 +1,4 @@
EXPORTS
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
DllCanUnloadNow PRIVATE
DllGetActivationFactory PRIVATE
DllGetClassObject PRIVATE

View File

@@ -8,8 +8,8 @@
<!-- build a dll, not exe (Application) -->
<ConfigurationType>DynamicLibrary</ConfigurationType>
<SubSystem>Console</SubSystem>
<!-- sets a bunch of Windows Universal properties -->
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
<!-- suppress a bunch of Windows Universal properties from cppwinrt.props -->
<OpenConsoleUniversalApp>false</OpenConsoleUniversalApp>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
@@ -21,23 +21,13 @@
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="OpenTerminalHere.h" />
<ClInclude Include="PlaceholderType.h">
<DependentUpon>PlaceholderType.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="PlaceholderType.cpp">
<DependentUpon>PlaceholderType.idl</DependentUpon>
</ClCompile>
<ClCompile Include="OpenTerminalHere.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="PlaceholderType.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@@ -58,5 +48,40 @@
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<!-- Override GetPackagingOutputs to roll up our DLL.
This is a heavily stripped version of the one in Microsoft.*.AppxPackage.targets.
-->
<PropertyGroup>
<_ContinueOnError Condition="'$(BuildingProject)' == 'true'">true</_ContinueOnError>
<_ContinueOnError Condition="'$(BuildingProject)' != 'true'">false</_ContinueOnError>
</PropertyGroup>
<Target Name="GetPackagingOutputs" Returns="@(PackagingOutputs)">
<CallTarget Targets="BuiltProjectOutputGroup">
<Output TaskParameter="TargetOutputs" ItemName="_BuiltProjectOutputGroupOutput" />
</CallTarget>
<ItemGroup>
<_PackagingOutputsUnexpanded Include="%(_BuiltProjectOutputGroupOutput.FinalOutputPath)">
<TargetPath>%(_BuiltProjectOutputGroupOutput.TargetPath)</TargetPath>
<OutputGroup>BuiltProjectOutputGroup</OutputGroup>
<ProjectName>$(ProjectName)</ProjectName>
</_PackagingOutputsUnexpanded>
</ItemGroup>
<CallTarget Targets="DebugSymbolsProjectOutputGroup">
<Output TaskParameter="TargetOutputs" ItemName="_DebugSymbolsProjectOutputGroupOutput" />
</CallTarget>
<ItemGroup>
<_PackagingOutputsUnexpanded Include="%(_DebugSymbolsProjectOutputGroupOutput.FinalOutputPath)">
<OutputGroup>DebugSymbolsProjectOutputGroup</OutputGroup>
<ProjectName>$(ProjectName)</ProjectName>
</_PackagingOutputsUnexpanded>
</ItemGroup>
<ItemGroup>
<PackagingOutputs Include="@(_PackagingOutputsUnexpanded)">
<TargetPath>%(Filename)%(Extension)</TargetPath>
</PackagingOutputs>
</ItemGroup>
</Target>
</Project>

View File

@@ -4,22 +4,21 @@
#include "pch.h"
#include "OpenTerminalHere.h"
// For reference, see:
// * https://docs.microsoft.com/en-us/cpp/cppcx/wrl/how-to-create-a-classic-com-component-using-wrl?view=vs-2019
// * https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/move-to-winrt-from-wrl#porting-a-wrl-module-microsoftwrlmodule
//
// We don't need to implement DllGetActivationFactory or DllCanUnloadNow
// manually, since the generated module.g.cpp will handle it for us, and will
// handle our WRL types appropriately.
//
// We DO need to implement DllGetClassObject, because that's what explorer.exe
// will call to attempt to create a class factory for our shell extension. The
// CoCreatableClass macro in OpenTerminalHere.h will create the factory for us,
// so that the GetClassObject call will work like magic.
using namespace Microsoft::WRL;
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv)
STDAPI DllCanUnloadNow()
{
return Microsoft::WRL::Module<Microsoft::WRL::InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
}
STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory)
{
return Module<InProc>::GetModule().GetActivationFactory(activatableClassId, factory);
}
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _COM_Outptr_ void** ppv)
{
return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
}
STDAPI_(BOOL)

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.201017.1" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
</packages>

View File

@@ -35,8 +35,8 @@ the MIT License. See LICENSE in the project root for license information. -->
is unfortunately static. -->
<SolidColorBrush x:Name="ErrorTextBrush" Color="{ThemeResource SystemErrorTextColor}" />
<!-- Suppress all padding except left around the tabs. The TabView looks far better like this. -->
<Thickness x:Key="TabViewHeaderPadding">8,0,0,0</Thickness>
<!-- Suppress top padding -->
<Thickness x:Key="TabViewHeaderPadding">8,0,8,0</Thickness>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">

View File

@@ -244,8 +244,8 @@ namespace winrt::TerminalApp::implementation
{
if (const auto& realArgs = args.ActionArgs().try_as<SwitchToTabArgs>())
{
const auto handled = _SelectTab({ realArgs.TabIndex() });
args.Handled(handled);
_SelectTab({ realArgs.TabIndex() });
args.Handled(true);
}
}
@@ -544,8 +544,8 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_HandleTabSearch(const IInspectable& /*sender*/,
const ActionEventArgs& args)
void TerminalPage::_HandleOpenTabSearch(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
CommandPalette().SetTabs(_tabs, _mruTabs);
CommandPalette().EnableTabSearchMode();
@@ -668,59 +668,4 @@ namespace winrt::TerminalApp::implementation
actionArgs.Handled(true);
}
void TerminalPage::_HandleIdentifyWindows(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
// Raise a IdentifyWindowsRequested event. This will bubble up to the
// AppLogic, to the AppHost, to the Peasant, to the Monarch, then get
// distributed down to _all_ the Peasants, as to display info about the
// window in _every_ Peasant window.
//
// This action is also buggy right now, because TeachingTips behave
// weird in XAML Islands. See microsoft-ui-xaml#4382
_IdentifyWindowsRequestedHandlers(*this, nullptr);
args.Handled(true);
}
void TerminalPage::_HandleIdentifyWindow(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
// Unlike _HandleIdentifyWindow**s**, this event just displays the
// window ID and name in the current window.
IdentifyWindow();
args.Handled(true);
}
void TerminalPage::_HandleRenameWindow(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<RenameWindowArgs>())
{
auto newName = realArgs.Name();
auto request = winrt::make_self<implementation::RenameWindowRequestedArgs>(newName);
_RenameWindowRequestedHandlers(*this, *request);
}
}
args.Handled(false);
}
void TerminalPage::_HandleOpenWindowRenamer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
WindowRenamer().IsOpen(true);
// PAIN: We can't immediately focus the textbox in the TeachingTip. It's
// not technically focusable until it is opened. However, it doesn't
// provide an even tto tell us when it is opened. That's tracked in
// microsoft/microsoft-ui-xaml#1607. So for now, the user _needs_ to
// click on the text box manually.
//
// We're also not using a ContentDialog for this, because in Xaml
// Islands a text box in a ContentDialog won't recieve _any_ keypresses.
// Fun!
// WindowRenamerTextBox().Focus(FocusState::Programmatic);
args.Handled(true);
}
}

View File

@@ -185,9 +185,9 @@ void AppCommandlineArgs::_buildParser()
maximized->excludes(fullscreen);
focus->excludes(fullscreen);
_app.add_option("-w,--window",
_windowTarget,
RS_A(L"CmdWindowTargetArgDesc"));
_app.add_option<std::optional<int>, int>("-w,--window",
_windowTarget,
RS_A(L"CmdWindowTargetArgDesc"));
// Subcommands
_buildNewTabParser();
@@ -854,16 +854,16 @@ void AppCommandlineArgs::FullResetState()
_exitMessage = "";
_shouldExitEarly = false;
_windowTarget = "";
_windowTarget = std::nullopt;
}
std::string_view AppCommandlineArgs::GetTargetWindow() const noexcept
std::optional<int> AppCommandlineArgs::GetTargetWindow() const noexcept
{
// // If the user provides _any_ negative number, then treat it as -1, for "use a new window".
// if (_windowTarget.has_value() && *_windowTarget < 0)
// {
// return { -1 };
// }
// If the user provides _any_ negative number, then treat it as -1, for "use a new window".
if (_windowTarget.has_value() && *_windowTarget < 0)
{
return { -1 };
}
return _windowTarget;
}

View File

@@ -44,7 +44,7 @@ public:
void DisableHelpInExitMessage();
void FullResetState();
std::string_view GetTargetWindow() const noexcept;
std::optional<int> GetTargetWindow() const noexcept;
private:
static const std::wregex _commandDelimiterRegex;
@@ -106,7 +106,7 @@ private:
std::string _exitMessage;
bool _shouldExitEarly{ false };
std::string _windowTarget{ "" };
std::optional<int> _windowTarget{ std::nullopt };
// Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand);

Some files were not shown because too many files have changed in this diff Show More