-
Notifications
You must be signed in to change notification settings - Fork 8.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix panes being dropped when tearing off tabs #18627
Conversation
{ | ||
actionArgs.Handled(true); | ||
ProcessStartupActions(actions, false); | ||
ProcessStartupActions(std::move(actions), false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use IVector
, when everything from start to finish in this stack talks in std::vector
already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you ugh
@@ -375,7 +374,7 @@ namespace winrt::TerminalApp::implementation | |||
// - <none> | |||
void TerminalPage::HandoffToElevated(const CascadiaSettings& settings) | |||
{ | |||
if (!_startupActions) | |||
if (_startupActions.empty()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure this was a bug. Handing off to elevated with 0 tabs doesn't seem useful to me.
|
||
// Handle it on a subsequent pass of the UI thread. | ||
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); | ||
const auto strong = get_strong(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplified lifetime management.
{ | ||
// We didn't actually need to change the virtual CWD, so we don't | ||
// need to restore it | ||
restoreCwd.release(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subjectively speaking, I found this highly confusing. Rewrote it to be a literal translation of the comment above.
(= "If the caller provided a CWD, [...] switch back once we're done" = if (!cwd.empty())
)
// Curiously, this does not seem to be required when using startupActions, but only when | ||
// tearing out a tab (this currently creates a new window with injected startup actions). | ||
// This indicates that this is really more of an architectural issue and not a fundamental one. | ||
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Low); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug fix. If the rest is too much, I'll extract this.
@@ -1200,7 +1196,7 @@ namespace winrt::TerminalApp::implementation | |||
{ | |||
try | |||
{ | |||
const auto& args = ActionAndArgs::Deserialize(content); | |||
const auto args = ActionAndArgs::Deserialize(content); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C++ is a whack language so be careful when binding return values to references. Doing so incorrectly breaks return-value-optimization (as it did here)!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/cc @carlos-zamora this is an excellent example of where const auto&
on a local goes wrong!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Double-checking) So what's the lesson here? Don't store returned IVector
s (or any winrt collection) to a const auto&
? Does it still work correctly, but it just gets rid of the optimization?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you have a function that returns something by-value and you bind it to a reference here's what actually happens:
const auto __hidden_variable__ = ActionAndArgs::Deserialize(content);
const auto& args = __hidden_variable__;
// ...
return args;
As you can see in the example, you return a reference at the end, not the original object. This forces C++ to make a copy of the object. If you don't bind it to a reference, then you'll also return the original object at the end and "return value optimization" will kick in.
The problem is when you have a function that returns a reference, but you bind it to a value. Because then you'll also make a copy:
const auto args = return_a_reference_to_arguments();
// ^ ^
// | Because `args` is a value type, but the function returns a reference,
// | this invokes the `vector(const vector& other)` copy constructor.
// |
// "auto" always infers a value type, never refences, etc.
// So, in this case it may be `std::vector`, not `std::vector&`.
C++ has a fix for this con job of supposed type inference and it's this:
decltype(auto) args = return_a_reference_to_arguments();
// ^
// This always infers the exact type, including references, constness, pointers, etc.
It couldn't have been just let
or var
or something. 😐
In any case, if you use Resharper (currently free to use via the so-called EAP), you get little inlay hints that tell you when an invisible copy constructor is called. This allows me to quickly spot these issues.
176beba
to
3ee9efd
Compare
3ee9efd
to
472e608
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! :)
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions; | ||
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ever since you told me to use std::vector, it's been great btw. Love to actually be able to see what's inside a vector. Big fan of this change!
@@ -1200,7 +1196,7 @@ namespace winrt::TerminalApp::implementation | |||
{ | |||
try | |||
{ | |||
const auto& args = ActionAndArgs::Deserialize(content); | |||
const auto args = ActionAndArgs::Deserialize(content); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Double-checking) So what's the lesson here? Don't store returned IVector
s (or any winrt collection) to a const auto&
? Does it still work correctly, but it just gets rid of the optimization?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! :)
I don't actually know why this is happening, because it doesn't happen with startup actions specified in the settings file. In any case, it's fixed with more delays. Closes #18572 ## Validation Steps Performed * Create a tab with 2 panes * Tear it off into a new window * New window has 1 tab with 2 panes ✅ (cherry picked from commit e1b28e7) Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXTsJY PVTI_lADOAF3p4s4AxadtzgXsQ6w Service-Version: 1.23
I don't actually know why this is happening, because it doesn't
happen with startup actions specified in the settings file.
In any case, it's fixed with more delays.
Closes #18572
Validation Steps Performed