Skip to content

Commit 7cda32d

Browse files
committed
make loading extensions async
1 parent 239bf69 commit 7cda32d

File tree

3 files changed

+117
-106
lines changed

3 files changed

+117
-106
lines changed

src/cascadia/TerminalSettingsEditor/Extensions.cpp

Lines changed: 112 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
3737
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
3838
_ViewModel = args.ViewModel().as<Editor::ExtensionsViewModel>();
3939

40+
// The extensions are loaded asynchronously as a part of the VM ctor.
41+
// However, there's a chance that they aren't done loading yet.
42+
// Calling LoadExtensions() ensures they're loaded by the time we try to display them, and it won't do anything if they're already loaded.
4043
auto vmImpl = get_self<ExtensionsViewModel>(_ViewModel);
44+
vmImpl->LoadExtensions();
4145
vmImpl->ExtensionPackageIdentifierTemplateSelector(_extensionPackageIdentifierTemplateSelector);
42-
vmImpl->LazyLoadExtensions();
4346

4447
BringIntoViewWhenLoaded(args.ElementToFocus());
4548

@@ -94,10 +97,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
9497

9598
ExtensionsViewModel::ExtensionsViewModel(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM) :
9699
_settings{ settings },
97-
_colorSchemesPageVM{ colorSchemesPageVM },
98-
_extensionsLoaded{ false }
100+
_colorSchemesPageVM{ colorSchemesPageVM }
99101
{
100102
UpdateSettings(settings, colorSchemesPageVM);
103+
_LoadExtensionsAsync();
101104

102105
PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) {
103106
const auto viewModelProperty{ args.PropertyName() };
@@ -217,7 +220,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
217220
_CurrentExtensionPackage = nullptr;
218221

219222
// The extension packages may not be loaded yet because we want to wait until we actually navigate to the page to do so.
220-
// In that case, omit "updating" them. They'll get the proper references when we lazy load them.
223+
// In that case, omit "updating" them. They'll get the proper references when we load them.
221224
if (_extensionPackages)
222225
{
223226
for (const auto& extPkg : _extensionPackages)
@@ -227,139 +230,146 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
227230
}
228231
}
229232

230-
void ExtensionsViewModel::LazyLoadExtensions()
233+
safe_void_coroutine ExtensionsViewModel::_LoadExtensionsAsync()
231234
{
232-
if (_extensionsLoaded)
235+
auto weakThis = get_weak();
236+
co_await winrt::resume_background();
237+
if (auto strongThis = weakThis.get())
233238
{
234-
return;
239+
strongThis->LoadExtensions();
235240
}
236-
std::vector<Model::ExtensionPackage> extensions = wil::to_vector(_settings.Extensions());
241+
}
237242

238-
// these vectors track components all extensions successfully added
239-
std::vector<Editor::ExtensionPackageViewModel> extensionPackages;
240-
std::vector<Editor::FragmentProfileViewModel> profilesModifiedTotal;
241-
std::vector<Editor::FragmentProfileViewModel> profilesAddedTotal;
242-
std::vector<Editor::FragmentColorSchemeViewModel> colorSchemesAddedTotal;
243-
for (const auto& extPkg : extensions)
244-
{
245-
auto extPkgVM = winrt::make_self<ExtensionPackageViewModel>(extPkg, _settings);
246-
for (const auto& fragExt : extPkg.FragmentsView())
243+
void ExtensionsViewModel::LoadExtensions()
244+
{
245+
std::call_once(_extensionsLoaded, [this]() {
246+
std::vector<Model::ExtensionPackage> extensions = wil::to_vector(_settings.Extensions());
247+
248+
// these vectors track components all extensions successfully added
249+
std::vector<Editor::ExtensionPackageViewModel> extensionPackages;
250+
std::vector<Editor::FragmentProfileViewModel> profilesModifiedTotal;
251+
std::vector<Editor::FragmentProfileViewModel> profilesAddedTotal;
252+
std::vector<Editor::FragmentColorSchemeViewModel> colorSchemesAddedTotal;
253+
for (const auto& extPkg : extensions)
247254
{
248-
const auto extensionEnabled = GetExtensionState(fragExt.Source(), _settings);
255+
auto extPkgVM = winrt::make_self<ExtensionPackageViewModel>(extPkg, _settings);
256+
for (const auto& fragExt : extPkg.FragmentsView())
257+
{
258+
const auto extensionEnabled = GetExtensionState(fragExt.Source(), _settings);
249259

250-
// these vectors track everything the current extension attempted to bring in
251-
std::vector<Editor::FragmentProfileViewModel> currentProfilesModified;
252-
std::vector<Editor::FragmentProfileViewModel> currentProfilesAdded;
253-
std::vector<Editor::FragmentColorSchemeViewModel> currentColorSchemesAdded;
260+
// these vectors track everything the current extension attempted to bring in
261+
std::vector<Editor::FragmentProfileViewModel> currentProfilesModified;
262+
std::vector<Editor::FragmentProfileViewModel> currentProfilesAdded;
263+
std::vector<Editor::FragmentColorSchemeViewModel> currentColorSchemesAdded;
254264

255-
if (fragExt.ModifiedProfilesView())
256-
{
257-
for (const auto&& entry : fragExt.ModifiedProfilesView())
265+
if (fragExt.ModifiedProfilesView())
258266
{
259-
// Ensure entry successfully modifies a profile before creating and registering the object
260-
if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid()))
267+
for (const auto&& entry : fragExt.ModifiedProfilesView())
261268
{
262-
auto vm = winrt::make<FragmentProfileViewModel>(entry, fragExt, deducedProfile);
263-
currentProfilesModified.push_back(vm);
264-
if (extensionEnabled)
269+
// Ensure entry successfully modifies a profile before creating and registering the object
270+
if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid()))
265271
{
266-
profilesModifiedTotal.push_back(vm);
272+
auto vm = winrt::make<FragmentProfileViewModel>(entry, fragExt, deducedProfile);
273+
currentProfilesModified.push_back(vm);
274+
if (extensionEnabled)
275+
{
276+
profilesModifiedTotal.push_back(vm);
277+
}
267278
}
268279
}
269280
}
270-
}
271281

272-
if (fragExt.NewProfilesView())
273-
{
274-
for (const auto&& entry : fragExt.NewProfilesView())
282+
if (fragExt.NewProfilesView())
275283
{
276-
// Ensure entry successfully points to a profile before creating and registering the object.
277-
// The profile may have been removed by the user.
278-
if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid()))
284+
for (const auto&& entry : fragExt.NewProfilesView())
279285
{
280-
auto vm = winrt::make<FragmentProfileViewModel>(entry, fragExt, deducedProfile);
281-
currentProfilesAdded.push_back(vm);
282-
if (extensionEnabled)
286+
// Ensure entry successfully points to a profile before creating and registering the object.
287+
// The profile may have been removed by the user.
288+
if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid()))
283289
{
284-
profilesAddedTotal.push_back(vm);
290+
auto vm = winrt::make<FragmentProfileViewModel>(entry, fragExt, deducedProfile);
291+
currentProfilesAdded.push_back(vm);
292+
if (extensionEnabled)
293+
{
294+
profilesAddedTotal.push_back(vm);
295+
}
285296
}
286297
}
287298
}
288-
}
289299

290-
if (fragExt.ColorSchemesView())
291-
{
292-
for (const auto&& entry : fragExt.ColorSchemesView())
300+
if (fragExt.ColorSchemesView())
293301
{
294-
for (const auto& schemeVM : _colorSchemesPageVM.AllColorSchemes())
302+
for (const auto&& entry : fragExt.ColorSchemesView())
295303
{
296-
if (schemeVM.Name() == entry.ColorSchemeName())
304+
for (const auto& schemeVM : _colorSchemesPageVM.AllColorSchemes())
297305
{
298-
auto vm = winrt::make<FragmentColorSchemeViewModel>(entry, fragExt, schemeVM);
299-
currentColorSchemesAdded.push_back(vm);
300-
if (extensionEnabled)
306+
if (schemeVM.Name() == entry.ColorSchemeName())
301307
{
302-
colorSchemesAddedTotal.push_back(vm);
308+
auto vm = winrt::make<FragmentColorSchemeViewModel>(entry, fragExt, schemeVM);
309+
currentColorSchemesAdded.push_back(vm);
310+
if (extensionEnabled)
311+
{
312+
colorSchemesAddedTotal.push_back(vm);
313+
}
303314
}
304315
}
305316
}
306317
}
307-
}
308318

309-
// sort the lists linguistically for nicer presentation
310-
std::sort(currentProfilesModified.begin(), currentProfilesModified.end(), FragmentProfileViewModel::SortAscending);
311-
std::sort(currentProfilesAdded.begin(), currentProfilesAdded.end(), FragmentProfileViewModel::SortAscending);
312-
std::sort(currentColorSchemesAdded.begin(), currentColorSchemesAdded.end(), FragmentColorSchemeViewModel::SortAscending);
319+
// sort the lists linguistically for nicer presentation
320+
std::sort(currentProfilesModified.begin(), currentProfilesModified.end(), FragmentProfileViewModel::SortAscending);
321+
std::sort(currentProfilesAdded.begin(), currentProfilesAdded.end(), FragmentProfileViewModel::SortAscending);
322+
std::sort(currentColorSchemesAdded.begin(), currentColorSchemesAdded.end(), FragmentColorSchemeViewModel::SortAscending);
313323

314-
extPkgVM->FragmentExtensions().Append(winrt::make<FragmentExtensionViewModel>(fragExt, currentProfilesModified, currentProfilesAdded, currentColorSchemesAdded));
315-
extPkgVM->PropertyChanged([&](const IInspectable& sender, const PropertyChangedEventArgs& args) {
316-
const auto viewModelProperty{ args.PropertyName() };
317-
if (viewModelProperty == L"Enabled")
318-
{
319-
// If the extension was enabled/disabled,
320-
// check if any of its fragments modified profiles, added profiles, or added color schemes.
321-
// Only notify what was affected!
322-
bool hasModifiedProfiles = false;
323-
bool hasAddedProfiles = false;
324-
bool hasAddedColorSchemes = false;
325-
for (const auto& fragExtVM : sender.as<ExtensionPackageViewModel>()->FragmentExtensions())
324+
extPkgVM->FragmentExtensions().Append(winrt::make<FragmentExtensionViewModel>(fragExt, currentProfilesModified, currentProfilesAdded, currentColorSchemesAdded));
325+
extPkgVM->PropertyChanged([&](const IInspectable& sender, const PropertyChangedEventArgs& args) {
326+
const auto viewModelProperty{ args.PropertyName() };
327+
if (viewModelProperty == L"Enabled")
326328
{
327-
const auto profilesModified = fragExtVM.ProfilesModified();
328-
const auto profilesAdded = fragExtVM.ProfilesAdded();
329-
const auto colorSchemesAdded = fragExtVM.ColorSchemesAdded();
330-
hasModifiedProfiles |= profilesModified && profilesModified.Size() > 0;
331-
hasAddedProfiles |= profilesAdded && profilesAdded.Size() > 0;
332-
hasAddedColorSchemes |= colorSchemesAdded && colorSchemesAdded.Size() > 0;
333-
}
334-
if (hasModifiedProfiles)
335-
{
336-
_NotifyChanges(L"ProfilesModified");
337-
}
338-
if (hasAddedProfiles)
339-
{
340-
_NotifyChanges(L"ProfilesAdded");
341-
}
342-
if (hasAddedColorSchemes)
343-
{
344-
_NotifyChanges(L"ColorSchemesAdded");
329+
// If the extension was enabled/disabled,
330+
// check if any of its fragments modified profiles, added profiles, or added color schemes.
331+
// Only notify what was affected!
332+
bool hasModifiedProfiles = false;
333+
bool hasAddedProfiles = false;
334+
bool hasAddedColorSchemes = false;
335+
for (const auto& fragExtVM : sender.as<ExtensionPackageViewModel>()->FragmentExtensions())
336+
{
337+
const auto profilesModified = fragExtVM.ProfilesModified();
338+
const auto profilesAdded = fragExtVM.ProfilesAdded();
339+
const auto colorSchemesAdded = fragExtVM.ColorSchemesAdded();
340+
hasModifiedProfiles |= profilesModified && profilesModified.Size() > 0;
341+
hasAddedProfiles |= profilesAdded && profilesAdded.Size() > 0;
342+
hasAddedColorSchemes |= colorSchemesAdded && colorSchemesAdded.Size() > 0;
343+
}
344+
if (hasModifiedProfiles)
345+
{
346+
_NotifyChanges(L"ProfilesModified");
347+
}
348+
if (hasAddedProfiles)
349+
{
350+
_NotifyChanges(L"ProfilesAdded");
351+
}
352+
if (hasAddedColorSchemes)
353+
{
354+
_NotifyChanges(L"ColorSchemesAdded");
355+
}
345356
}
346-
}
347-
});
357+
});
358+
}
359+
extensionPackages.push_back(*extPkgVM);
348360
}
349-
extensionPackages.push_back(*extPkgVM);
350-
}
351361

352-
// sort the lists linguistically for nicer presentation
353-
std::sort(extensionPackages.begin(), extensionPackages.end(), ExtensionPackageViewModel::SortAscending);
354-
std::sort(profilesModifiedTotal.begin(), profilesModifiedTotal.end(), FragmentProfileViewModel::SortAscending);
355-
std::sort(profilesAddedTotal.begin(), profilesAddedTotal.end(), FragmentProfileViewModel::SortAscending);
356-
std::sort(colorSchemesAddedTotal.begin(), colorSchemesAddedTotal.end(), FragmentColorSchemeViewModel::SortAscending);
357-
358-
_extensionPackages = single_threaded_observable_vector<Editor::ExtensionPackageViewModel>(std::move(extensionPackages));
359-
_profilesModifiedView = single_threaded_observable_vector<Editor::FragmentProfileViewModel>(std::move(profilesModifiedTotal));
360-
_profilesAddedView = single_threaded_observable_vector<Editor::FragmentProfileViewModel>(std::move(profilesAddedTotal));
361-
_colorSchemesAddedView = single_threaded_observable_vector<Editor::FragmentColorSchemeViewModel>(std::move(colorSchemesAddedTotal));
362-
_extensionsLoaded = true;
362+
// sort the lists linguistically for nicer presentation
363+
std::sort(extensionPackages.begin(), extensionPackages.end(), ExtensionPackageViewModel::SortAscending);
364+
std::sort(profilesModifiedTotal.begin(), profilesModifiedTotal.end(), FragmentProfileViewModel::SortAscending);
365+
std::sort(profilesAddedTotal.begin(), profilesAddedTotal.end(), FragmentProfileViewModel::SortAscending);
366+
std::sort(colorSchemesAddedTotal.begin(), colorSchemesAddedTotal.end(), FragmentColorSchemeViewModel::SortAscending);
367+
368+
_extensionPackages = single_threaded_observable_vector<Editor::ExtensionPackageViewModel>(std::move(extensionPackages));
369+
_profilesModifiedView = single_threaded_observable_vector<Editor::FragmentProfileViewModel>(std::move(profilesModifiedTotal));
370+
_profilesAddedView = single_threaded_observable_vector<Editor::FragmentProfileViewModel>(std::move(profilesAddedTotal));
371+
_colorSchemesAddedView = single_threaded_observable_vector<Editor::FragmentColorSchemeViewModel>(std::move(colorSchemesAddedTotal));
372+
});
363373
}
364374

365375
Windows::UI::Xaml::DataTemplate ExtensionsViewModel::CurrentExtensionPackageIdentifierTemplate() const

src/cascadia/TerminalSettingsEditor/Extensions.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
4848
bool NoSchemesAdded() const noexcept { return _colorSchemesAddedView.Size() == 0; }
4949

5050
// Views
51-
Windows::Foundation::Collections::IObservableVector<Editor::ExtensionPackageViewModel> ExtensionPackages() const noexcept { return _extensionPackages; }
51+
// NOTE: the extensions are being loaded asynchronously in the ctor. _extensionPackages may be null in that brief window of time.
52+
Windows::Foundation::Collections::IObservableVector<Editor::ExtensionPackageViewModel> ExtensionPackages() const noexcept { return _extensionPackages ? _extensionPackages : single_threaded_observable_vector<Editor::ExtensionPackageViewModel>(); }
5253
Windows::Foundation::Collections::IObservableVector<Editor::FragmentProfileViewModel> ProfilesModified() const noexcept { return _profilesModifiedView; }
5354
Windows::Foundation::Collections::IObservableVector<Editor::FragmentProfileViewModel> ProfilesAdded() const noexcept { return _profilesAddedView; }
5455
Windows::Foundation::Collections::IObservableVector<Editor::FragmentColorSchemeViewModel> ColorSchemesAdded() const noexcept { return _colorSchemesAddedView; }
5556

5657
// Methods
57-
void LazyLoadExtensions();
58+
void LoadExtensions();
5859
void UpdateSettings(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM);
5960
void NavigateToProfile(const guid profileGuid);
6061
void NavigateToColorScheme(const Editor::ColorSchemeViewModel& schemeVM);
@@ -75,8 +76,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
7576
Windows::Foundation::Collections::IObservableVector<Editor::FragmentProfileViewModel> _profilesModifiedView;
7677
Windows::Foundation::Collections::IObservableVector<Editor::FragmentProfileViewModel> _profilesAddedView;
7778
Windows::Foundation::Collections::IObservableVector<Editor::FragmentColorSchemeViewModel> _colorSchemesAddedView;
78-
bool _extensionsLoaded;
79+
std::once_flag _extensionsLoaded;
7980

81+
safe_void_coroutine _LoadExtensionsAsync();
8082
void _UpdateListViews(bool updateProfilesModified, bool updateProfilesAdded, bool updateColorSchemesAdded);
8183
};
8284

0 commit comments

Comments
 (0)