19
19
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
20
20
#include " SshHostGenerator.h"
21
21
#endif
22
+ #include " PowershellInstallationProfileGenerator.h"
22
23
23
24
#include " ApplicationState.h"
24
25
#include " DefaultTerminal.h"
@@ -209,26 +210,57 @@ Json::StreamWriterBuilder SettingsLoader::_getJsonStyledWriter()
209
210
// (meaning profiles specified by the application rather by the user).
210
211
void SettingsLoader::GenerateProfiles ()
211
212
{
212
- auto generateProfiles = [&](const IDynamicProfileGenerator& generator) {
213
+ auto generateProfiles = [&]<typename T>() {
214
+ T generator{};
213
215
if (!_ignoredNamespaces.contains (generator.GetNamespace ()))
214
216
{
215
217
_executeGenerator (generator, inboxSettings.profiles );
216
218
}
219
+ return generator;
217
220
};
218
221
219
- generateProfiles (PowershellCoreProfileGenerator{});
220
- generateProfiles (WslDistroGenerator{});
221
- generateProfiles (AzureCloudShellGenerator{});
222
- generateProfiles (VisualStudioGenerator{});
222
+ bool isPowerShellInstalled;
223
+ {
224
+ auto powerShellGenerator = generateProfiles.template operator ()<PowershellCoreProfileGenerator>();
225
+ isPowerShellInstalled = !powerShellGenerator.GetPowerShellInstances ().empty ();
226
+ }
227
+
228
+ if (Feature_PowerShellInstallerProfileGenerator::IsEnabled ())
229
+ {
230
+ if (isPowerShellInstalled)
231
+ {
232
+ // PowerShell is installed, mark the installer profile for deletion (if found)
233
+ const winrt::guid profileGuid{ L" {965a10f2-b0f2-55dc-a3c2-2ddbf639bf89}" };
234
+ for (const auto & profile : userSettings.profiles )
235
+ {
236
+ if (profile->Guid () == profileGuid)
237
+ {
238
+ profile->Deleted (true );
239
+ break ;
240
+ }
241
+ }
242
+ }
243
+ else
244
+ {
245
+ // PowerShell isn't installed --> generate the installer stub profile
246
+ generateProfiles.template operator ()<PowershellInstallationProfileGenerator>();
247
+ }
248
+ }
249
+
250
+ generateProfiles.template operator ()<WslDistroGenerator>();
251
+ generateProfiles.template operator ()<AzureCloudShellGenerator>();
252
+ generateProfiles.template operator ()<VisualStudioGenerator>();
253
+
223
254
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
224
- generateProfiles ( SshHostGenerator{} );
255
+ generateProfiles. template operator ()< SshHostGenerator>( );
225
256
#endif
226
257
}
227
258
228
259
// Generate ExtensionPackage objects from the profile generators.
229
260
void SettingsLoader::GenerateExtensionPackagesFromProfileGenerators ()
230
261
{
231
- auto generateExtensionPackages = [&](const IDynamicProfileGenerator& generator) {
262
+ auto generateExtensionPackages = [&]<typename T>() {
263
+ T generator{};
232
264
std::vector<winrt::com_ptr<implementation::Profile>> profilesList;
233
265
_executeGenerator (generator, profilesList);
234
266
@@ -253,19 +285,67 @@ void SettingsLoader::GenerateExtensionPackagesFromProfileGenerators()
253
285
auto extPkg = _registerFragment (std::move (*generatorExtension), FragmentScope::Machine);
254
286
extPkg->DisplayName (hstring{ generator.GetDisplayName () });
255
287
extPkg->Icon (hstring{ generator.GetIcon () });
288
+ return generator;
256
289
};
257
290
258
- // TODO CARLOS: is there a way to deduplicate this list?
259
- // Is it even worth it if we're adding special logic for the PwshInstallerGenerator PR?
260
- generateExtensionPackages (PowershellCoreProfileGenerator{});
261
- generateExtensionPackages (WslDistroGenerator{});
262
- generateExtensionPackages (AzureCloudShellGenerator{});
263
- generateExtensionPackages (VisualStudioGenerator{});
291
+ bool isPowerShellInstalled;
292
+ {
293
+ auto powerShellGenerator = generateExtensionPackages.template operator ()<PowershellCoreProfileGenerator>();
294
+ isPowerShellInstalled = !powerShellGenerator.GetPowerShellInstances ().empty ();
295
+ }
296
+ if (Feature_PowerShellInstallerProfileGenerator::IsEnabled ())
297
+ {
298
+ generateExtensionPackages.template operator ()<PowershellInstallationProfileGenerator>();
299
+ _patchInstallPowerShellProfile (isPowerShellInstalled);
300
+ }
301
+
302
+ generateExtensionPackages.template operator ()<WslDistroGenerator>();
303
+ generateExtensionPackages.template operator ()<AzureCloudShellGenerator>();
304
+ generateExtensionPackages.template operator ()<VisualStudioGenerator>();
264
305
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
265
- generateExtensionPackages ( SshHostGenerator{} );
306
+ generateExtensionPackages. template operator ()< SshHostGenerator>( );
266
307
#endif
267
308
}
268
309
310
+ // Retrieve the "Install Latest PowerShell" profile and add a comment to the JSON to indicate it's conditionally applied.
311
+ // If PowerShell is installed, delete the profile from the extension package.
312
+ void SettingsLoader::_patchInstallPowerShellProfile (bool isPowerShellInstalled)
313
+ {
314
+ const hstring pwshInstallerNamespace{ PowershellInstallationProfileGenerator::Namespace };
315
+ if (extensionPackageMap.contains (pwshInstallerNamespace))
316
+ {
317
+ if (const auto & fragExtList = extensionPackageMap[pwshInstallerNamespace]->Fragments (); fragExtList.Size () > 0 )
318
+ {
319
+ auto fragExt = get_self<FragmentSettings>(fragExtList.GetAt (0 ));
320
+
321
+ // We want the comment to be the first thing in the object,
322
+ // "closeOnExit" is the first property, so target that.
323
+ auto fragExtJson = _parseJSON (til::u16u8 (fragExt->Json ()));
324
+ fragExtJson[JsonKey (ProfilesKey)][0 ][" closeOnExit" ].setComment (til::u16u8 (fmt::format (FMT_COMPILE (L" // {}" ), RS_ (L" PowerShellInstallationProfileJsonComment" ))), Json::CommentPlacement::commentBefore);
325
+ fragExt->Json (hstring{ til::u8u16 (Json::writeString (_getJsonStyledWriter (), fragExtJson)) });
326
+
327
+ if (const auto & profileEntryList = fragExt->NewProfiles (); profileEntryList.Size () > 0 )
328
+ {
329
+ if (isPowerShellInstalled)
330
+ {
331
+ // PowerShell is installed, so the installer profile was marked for deletion in GenerateProfiles().
332
+ // Remove the profile object from the fragment so it doesn't show up in the settings UI.
333
+ profileEntryList.RemoveAt (0 );
334
+ }
335
+ else
336
+ {
337
+ // We want the comment to be the first thing in the object,
338
+ // "closeOnExit" is the first property, so target that.
339
+ auto profileEntry = get_self<FragmentProfileEntry>(profileEntryList.GetAt (0 ));
340
+ auto profileJson = _parseJSON (til::u16u8 (profileEntry->Json ()));
341
+ profileJson[" closeOnExit" ].setComment (til::u16u8 (fmt::format (FMT_COMPILE (L" // {}" ), RS_ (L" PowerShellInstallationProfileJsonComment" ))), Json::CommentPlacement::commentBefore);
342
+ profileEntry->Json (hstring{ til::u8u16 (Json::writeString (_getJsonStyledWriter (), profileJson)) });
343
+ }
344
+ }
345
+ }
346
+ }
347
+ }
348
+
269
349
// A new settings.json gets a special treatment:
270
350
// 1. The default profile is a PowerShell 7+ one, if one was generated,
271
351
// and falls back to the standard PowerShell 5 profile otherwise.
@@ -1084,7 +1164,7 @@ bool SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptr<implementat
1084
1164
1085
1165
// As the name implies it executes a generator.
1086
1166
// Generated profiles are added to .inboxSettings. Used by GenerateProfiles().
1087
- void SettingsLoader::_executeGenerator (const IDynamicProfileGenerator& generator, std::vector<winrt::com_ptr<implementation::Profile>>& profilesList)
1167
+ void SettingsLoader::_executeGenerator (IDynamicProfileGenerator& generator, std::vector<winrt::com_ptr<implementation::Profile>>& profilesList)
1088
1168
{
1089
1169
const auto generatorNamespace = generator.GetNamespace ();
1090
1170
const auto previousSize = profilesList.size ();
@@ -1665,7 +1745,11 @@ void CascadiaSettings::_resolveNewTabMenuProfiles() const
1665
1745
auto activeProfileCount = gsl::narrow_cast<int >(_activeProfiles.Size ());
1666
1746
for (auto profileIndex = 0 ; profileIndex < activeProfileCount; profileIndex++)
1667
1747
{
1668
- remainingProfilesMap.emplace (profileIndex, _activeProfiles.GetAt (profileIndex));
1748
+ const auto & profile = _activeProfiles.GetAt (profileIndex);
1749
+ if (!profile.Deleted ())
1750
+ {
1751
+ remainingProfilesMap.emplace (profileIndex, _activeProfiles.GetAt (profileIndex));
1752
+ }
1669
1753
}
1670
1754
1671
1755
// We keep track of the "remaining profiles" - those that have not yet been resolved
0 commit comments