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"
@@ -210,28 +211,58 @@ Json::StreamWriterBuilder SettingsLoader::_getJsonStyledWriter()
210
211
// (meaning profiles specified by the application rather by the user).
211
212
void SettingsLoader::GenerateProfiles ()
212
213
{
213
- auto generateProfiles = [&](const IDynamicProfileGenerator& generator) {
214
+ auto generateProfiles = [&]<typename T>() {
215
+ T generator{};
214
216
if (!_ignoredNamespaces.contains (generator.GetNamespace ()))
215
217
{
216
218
_executeGenerator (generator, inboxSettings.profiles );
217
219
}
220
+ return generator;
218
221
};
219
222
223
+ bool isPowerShellInstalled;
224
+ {
225
+ auto powerShellGenerator = generateProfiles.template operator ()<PowershellCoreProfileGenerator>();
226
+ isPowerShellInstalled = !powerShellGenerator.GetPowerShellInstances ().empty ();
227
+ }
228
+
229
+ if (Feature_PowerShellInstallerProfileGenerator::IsEnabled ())
230
+ {
231
+ if (isPowerShellInstalled)
232
+ {
233
+ // PowerShell is installed, mark the installer profile for deletion (if found)
234
+ const winrt::guid profileGuid{ L" {965a10f2-b0f2-55dc-a3c2-2ddbf639bf89}" };
235
+ for (const auto & profile : userSettings.profiles )
236
+ {
237
+ if (profile->Guid () == profileGuid)
238
+ {
239
+ profile->Deleted (true );
240
+ break ;
241
+ }
242
+ }
243
+ }
244
+ else
245
+ {
246
+ // PowerShell isn't installed --> generate the installer stub profile
247
+ generateProfiles.template operator ()<PowershellInstallationProfileGenerator>();
248
+ }
249
+ }
250
+
220
251
// Generate profiles for each generator and add them to the inbox settings.
221
252
// Be sure to update the same list below.
222
- generateProfiles (PowershellCoreProfileGenerator{});
223
- generateProfiles (WslDistroGenerator{});
224
- generateProfiles (AzureCloudShellGenerator{});
225
- generateProfiles (VisualStudioGenerator{});
253
+ generateProfiles.template operator ()<WslDistroGenerator>();
254
+ generateProfiles.template operator ()<AzureCloudShellGenerator>();
255
+ generateProfiles.template operator ()<VisualStudioGenerator>();
226
256
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
227
- generateProfiles ( SshHostGenerator{} );
257
+ generateProfiles. template operator ()< SshHostGenerator>( );
228
258
#endif
229
259
}
230
260
231
261
// Generate ExtensionPackage objects from the profile generators.
232
262
void SettingsLoader::GenerateExtensionPackagesFromProfileGenerators ()
233
263
{
234
- auto generateExtensionPackages = [&](const IDynamicProfileGenerator& generator) {
264
+ auto generateExtensionPackages = [&]<typename T>() {
265
+ T generator{};
235
266
std::vector<winrt::com_ptr<implementation::Profile>> profilesList;
236
267
_executeGenerator (generator, profilesList);
237
268
@@ -256,19 +287,69 @@ void SettingsLoader::GenerateExtensionPackagesFromProfileGenerators()
256
287
auto extPkg = _registerFragment (std::move (*generatorExtension), FragmentScope::Machine);
257
288
extPkg->DisplayName (hstring{ generator.GetDisplayName () });
258
289
extPkg->Icon (hstring{ generator.GetIcon () });
290
+ return generator;
259
291
};
260
292
293
+ bool isPowerShellInstalled;
294
+ {
295
+ auto powerShellGenerator = generateExtensionPackages.template operator ()<PowershellCoreProfileGenerator>();
296
+ isPowerShellInstalled = !powerShellGenerator.GetPowerShellInstances ().empty ();
297
+ }
298
+ if (Feature_PowerShellInstallerProfileGenerator::IsEnabled ())
299
+ {
300
+ generateExtensionPackages.template operator ()<PowershellInstallationProfileGenerator>();
301
+ _patchInstallPowerShellProfile (isPowerShellInstalled);
302
+ }
303
+
261
304
// Generate extension package objects for each generator.
262
305
// Be sure to update the same list above.
263
- generateExtensionPackages (PowershellCoreProfileGenerator{});
264
- generateExtensionPackages (WslDistroGenerator{});
265
- generateExtensionPackages (AzureCloudShellGenerator{});
266
- generateExtensionPackages (VisualStudioGenerator{});
306
+ generateExtensionPackages.template operator ()<WslDistroGenerator>();
307
+ generateExtensionPackages.template operator ()<AzureCloudShellGenerator>();
308
+ generateExtensionPackages.template operator ()<VisualStudioGenerator>();
267
309
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
268
- generateExtensionPackages ( SshHostGenerator{} );
310
+ generateExtensionPackages. template operator ()< SshHostGenerator>( );
269
311
#endif
270
312
}
271
313
314
+ // Retrieve the "Install Latest PowerShell" profile and add a comment to the JSON to indicate it's conditionally applied.
315
+ // If PowerShell is installed, delete the profile from the extension package.
316
+ void SettingsLoader::_patchInstallPowerShellProfile (bool isPowerShellInstalled)
317
+ {
318
+ const hstring pwshInstallerNamespace{ PowershellInstallationProfileGenerator::Namespace };
319
+ if (extensionPackageMap.contains (pwshInstallerNamespace))
320
+ {
321
+ if (const auto & fragExtList = extensionPackageMap[pwshInstallerNamespace]->Fragments (); fragExtList.Size () > 0 )
322
+ {
323
+ auto fragExt = get_self<FragmentSettings>(fragExtList.GetAt (0 ));
324
+
325
+ // We want the comment to be the first thing in the object,
326
+ // "closeOnExit" is the first property, so target that.
327
+ auto fragExtJson = _parseJSON (til::u16u8 (fragExt->Json ()));
328
+ fragExtJson[JsonKey (ProfilesKey)][0 ][" closeOnExit" ].setComment (til::u16u8 (fmt::format (FMT_COMPILE (L" // {}" ), RS_ (L" PowerShellInstallationProfileJsonComment" ))), Json::CommentPlacement::commentBefore);
329
+ fragExt->Json (hstring{ til::u8u16 (Json::writeString (_getJsonStyledWriter (), fragExtJson)) });
330
+
331
+ if (const auto & profileEntryList = fragExt->NewProfiles (); profileEntryList.Size () > 0 )
332
+ {
333
+ if (isPowerShellInstalled)
334
+ {
335
+ // PowerShell is installed, so the installer profile was marked for deletion in GenerateProfiles().
336
+ // Remove the profile object from the fragment so it doesn't show up in the settings UI.
337
+ profileEntryList.RemoveAt (0 );
338
+ }
339
+ else
340
+ {
341
+ // We want the comment to be the first thing in the object,
342
+ // "closeOnExit" is the first property, so target that.
343
+ auto profileEntry = get_self<FragmentProfileEntry>(profileEntryList.GetAt (0 ));
344
+ auto profileJson = _parseJSON (til::u16u8 (profileEntry->Json ()));
345
+ profileJson[" closeOnExit" ].setComment (til::u16u8 (fmt::format (FMT_COMPILE (L" // {}" ), RS_ (L" PowerShellInstallationProfileJsonComment" ))), Json::CommentPlacement::commentBefore);
346
+ profileEntry->Json (hstring{ til::u8u16 (Json::writeString (_getJsonStyledWriter (), profileJson)) });
347
+ }
348
+ }
349
+ }
350
+ }
351
+ }
352
+
272
353
// A new settings.json gets a special treatment:
273
354
// 1. The default profile is a PowerShell 7+ one, if one was generated,
274
355
// and falls back to the standard PowerShell 5 profile otherwise.
@@ -1095,7 +1176,7 @@ bool SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptr<implementat
1095
1176
1096
1177
// As the name implies it executes a generator.
1097
1178
// Generated profiles are added to .inboxSettings. Used by GenerateProfiles().
1098
- void SettingsLoader::_executeGenerator (const IDynamicProfileGenerator& generator, std::vector<winrt::com_ptr<implementation::Profile>>& profilesList)
1179
+ void SettingsLoader::_executeGenerator (IDynamicProfileGenerator& generator, std::vector<winrt::com_ptr<implementation::Profile>>& profilesList)
1099
1180
{
1100
1181
const auto generatorNamespace = generator.GetNamespace ();
1101
1182
const auto previousSize = profilesList.size ();
@@ -1679,7 +1760,11 @@ void CascadiaSettings::_resolveNewTabMenuProfiles() const
1679
1760
auto activeProfileCount = gsl::narrow_cast<int >(_activeProfiles.Size ());
1680
1761
for (auto profileIndex = 0 ; profileIndex < activeProfileCount; profileIndex++)
1681
1762
{
1682
- remainingProfilesMap.emplace (profileIndex, _activeProfiles.GetAt (profileIndex));
1763
+ const auto & profile = _activeProfiles.GetAt (profileIndex);
1764
+ if (!profile.Deleted ())
1765
+ {
1766
+ remainingProfilesMap.emplace (profileIndex, _activeProfiles.GetAt (profileIndex));
1767
+ }
1683
1768
}
1684
1769
1685
1770
// We keep track of the "remaining profiles" - those that have not yet been resolved
0 commit comments