Repository: microsoft/terminal (language: cpp)

Issue:
Re-registered WSL distro profiles do not get auto generated on terminal restart unless state.json is deleted.
### Windows Terminal version (or Windows build number)

Microsoft Windows [Version 10.0.19043.1237]

### Other Software

WSL

### Steps to reproduce

- close terminal
- re-register an existing WSL distribution which had an existing profile in WT before
- remove that profile from WT's config
- open terminal
- config file does NOT contain the new distribution's auto-generated profile

### Expected Behavior

An auto-generated profile for the WSL distro which was registered.

### Actual Behavior

A profile is not added automatically, to make WT add the profile, the state.json file must be deleted, and the terminal must be reopened.

I'm just blindly guessing but I'm assuming this bug as to do w caching existing profiles in state.json, wt looks at the guid in state.json, assumes thats already generated, and skips trying to generate it again for the config.

Also, just for my own curiosity, how are the GUIDs generated for a distro? Is it a uuid v5 with the terminal uuid namespace? I registered a custom distro using `wsl --import` and it gave it a GUID too. A uuid v5 is what i could tell from the source code, but i tried generating some UUID v5s myself using the terminal's runtime namespace and the name of the distro, and the results weren't the same.


Discussion / hints:
Ok so I may have found the cause,

Before:

![image](https://user-images.githubusercontent.com/67145097/137368092-4cc0921b-e68b-4b8e-a0da-692718a4171c.png)


As you can see, `state.json` has the `Ubuntu` distro's GUID in the `generatedProfiles` array.

Now lets assume that the user decides to uninstall Ubuntu, and hence removes the Ubuntu profile from `settings.json`,

![image](https://user-images.githubusercontent.com/67145097/137368291-74b52954-5df6-469e-ac6e-ed7f16a546d2.png)


As you can see, the GUID of `Ubuntu` isn't removed from `state.json` upon removing it from `settings.json`.

Now lets assume that the user decides to reinstall Ubuntu again, and restarts Windows Terminal, since the GUID of Ubuntu is still in the generatedProfiles array even thought the profile IS NOT generated, the Ubuntu profile won't be regenerated this time, hence causing the bug.

The workaround has been to remove the GUID of that distro which is still stuck in state.json and make any modification to the config causing the state.json file to be rewritten.
Okay yep, that's exactly what happened here. Same as in #11593.

1. We found the WSL profile, generated it into the settings.json file, and then added that guid to `state.json` so we wouldn't generate it again.
2. We saw that the distro was gone, so we _removed it from the file_
3. When we re-found the profile, we see that we already added it into the `state`, so we skip it.

We should maybe not do 2 anymore. @lhecker thoughts?
Yes, we should not do 2. Only now after #11184 merged do we have the ability to _find_ disconnected generator "leaf profiles." They used to be omitted from the settings model entirely at parse time.

I've mentioned to a couple of you a UI that would display something like, "This profile was generated and seems to have disappeared. We've *kept your local copy* just in case." etc.
(we only do 2 when you [save] while the distro is gone. That's still a big footgun.)
Oh right I meant to test this today...
If I remove my WSL distro and launch WT the distro is _not_ removed from the file. However hitting "Save" in the settings UI removes it, as only "visible" profiles are saved.
Implementing Dustin's suggestion shouldn't be too hard. This is the code responsible for removing such profiles: https://github.com/microsoft/terminal/blob/9662bc69102e2e152a062a9060cb63d2742582d3/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp#L776-L788

I'm not sure if it would be enough to simply mark such profiles as "hidden".
So i had this problem just now when re-registering Debian, i notice that it didn't created the Debian profile even when i wanted to duplicate.

To resolve what i did was delete all `generatedProfiles` in `state.json` that wasn't in use in `settings.json`, then i go to duplicate a profile, and this time Debian was a option.

Fix patch:
diff --git a/src/cascadia/TerminalSettingsEditor/LaunchViewModel.cpp b/src/cascadia/TerminalSettingsEditor/LaunchViewModel.cpp
index 29364e0e61f..8dc4c59dbc7 100644
--- a/src/cascadia/TerminalSettingsEditor/LaunchViewModel.cpp
+++ b/src/cascadia/TerminalSettingsEditor/LaunchViewModel.cpp
@@ -332,7 +332,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         // from menus, but still work as the startup profile for instance.
         for (const auto& profile : allProfiles)
         {
-            if (!profile.Deleted())
+            if (!profile.Deleted() && !profile.Orphaned() /* BACKPORT GH#18188 - DO NOT DISPLAY ORPHANED PROFILES */)
             {
                 profiles.emplace_back(profile);
             }
diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.cpp b/src/cascadia/TerminalSettingsEditor/MainPage.cpp
index f7100bacc71..359dc50fd9f 100644
--- a/src/cascadia/TerminalSettingsEditor/MainPage.cpp
+++ b/src/cascadia/TerminalSettingsEditor/MainPage.cpp
@@ -526,7 +526,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
         // profile changes.
         for (const auto& profile : _settingsClone.AllProfiles())
         {
-            if (!profile.Deleted())
+            if (!profile.Deleted() && !profile.Orphaned() /* BACKPORT GH#18188 - DO NOT DISPLAY ORPHANED PROFILES */)
             {
                 auto profileVM = _viewModelForProfile(profile, _settingsClone);
                 profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
index 6b412d28b68..eeb3d7cc2e3 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
@@ -103,7 +103,7 @@ Model::CascadiaSettings CascadiaSettings::Copy() const
             for (const auto& profile : targetProfiles)
             {
                 allProfiles.emplace_back(*profile);
-                if (!profile->Hidden())
+                if (!profile->Hidden() && !profile->Orphaned())
                 {
                     activeProfiles.emplace_back(*profile);
                 }
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
index 1039bf4343d..176eda6d1b0 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
@@ -1217,12 +1217,12 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) :
             const auto& parents = profile->Parents();
             if (std::none_of(parents.begin(), parents.end(), [&](const auto& parent) { return parent->Source() == source; }))
             {
-                continue;
+                profile->Orphaned(true);
             }
         }
 
         allProfiles.emplace_back(*profile);
-        if (!profile->Hidden())
+        if (!profile->Hidden() && !profile->Orphaned())
         {
             activeProfiles.emplace_back(*profile);
         }
diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp
index 3d1d2782472..1aac435686d 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.cpp
+++ b/src/cascadia/TerminalSettingsModel/Profile.cpp
@@ -103,6 +103,7 @@ winrt::com_ptr<Profile> Profile::CopySettings() const
     const auto defaultAppearance = AppearanceConfig::CopyAppearance(winrt::get_self<AppearanceConfig>(_DefaultAppearance), weakProfile);
 
     profile->_Deleted = _Deleted;
+    profile->_Orphaned = _Orphaned;
     profile->_Updates = _Updates;
     profile->_Guid = _Guid;
     profile->_Name = _Name;
diff --git a/src/cascadia/TerminalSettingsModel/Profile.h b/src/cascadia/TerminalSettingsModel/Profile.h
index 24fbb7d1f2d..86110c206ea 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.h
+++ b/src/cascadia/TerminalSettingsModel/Profile.h
@@ -115,6 +115,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         void Icon(const hstring& value);
 
         WINRT_PROPERTY(bool, Deleted, false);
+        WINRT_PROPERTY(bool, Orphaned, false);
         WINRT_PROPERTY(OriginTag, Origin, OriginTag::None);
         WINRT_PROPERTY(guid, Updates);
 
diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl
index 1ba21c4086c..76ab6f5e045 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.idl
+++ b/src/cascadia/TerminalSettingsModel/Profile.idl
@@ -41,6 +41,8 @@ namespace Microsoft.Terminal.Settings.Model
 
         // True if the user explicitly removed this Profile from settings.json.
         Boolean Deleted { get; };
+        // True if the user *kept* this Profile, but it disappeared from the system.
+        Boolean Orphaned { get; };
 
         // Helper for magically using a commandline for an icon for a profile
         // without an explicit icon.


Test patch: