Skip to content

Commit 89ac86f

Browse files
committed
Iterate on JsonManager (needs review)
1 parent 7bf7684 commit 89ac86f

13 files changed

Lines changed: 264 additions & 168 deletions

File tree

src/cascadia/TerminalApp/AppActionHandlers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1318,7 +1318,7 @@ namespace winrt::TerminalApp::implementation
13181318
keyChord = KeyChordSerialization::FromString(winrt::to_hstring(realArgs.KeyChord()));
13191319
}
13201320
_settings.GlobalSettings().ActionMap().AddSendInputAction(realArgs.Name(), commandLine, keyChord);
1321-
_settings.WriteSettingsToDisk();
1321+
JsonManager::WriteSettings(_settings);
13221322
ActionSaved(commandLine, realArgs.Name(), realArgs.KeyChord());
13231323
}
13241324
catch (const winrt::hresult_error& ex)

src/cascadia/TerminalApp/TerminalPage.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,9 @@ namespace winrt::TerminalApp::implementation
951951
if (result == ContentDialogResult::Primary && checkbox.IsChecked().Value())
952952
{
953953
_settings.GlobalSettings().ConfirmOnClose(ConfirmOnClose::Never);
954-
_settings.WriteSettingsToDisk();
954+
// Persisted via auto-save: the setter fires the write sink, which
955+
// updates the JsonManager baseline and avoids a redundant write +
956+
// spurious reload (GH#12424). A pending write is flushed on exit.
955957
}
956958
}
957959

src/cascadia/TerminalSettingsEditor/Compatibility.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
4545
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
4646
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
4747

48-
_settings.ResetToDefaultSettings();
48+
JsonManager::ResetToDefaultSettings();
4949
}
5050

5151
Compatibility::Compatibility()

src/cascadia/TerminalSettingsEditor/MainPage.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -861,9 +861,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
861861
void MainPage::SaveButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
862862
{
863863
_settingsClone.LogSettingChanges(false);
864-
if (!_settingsClone.WriteSettingsToDisk())
864+
if (JsonManager::WriteSettings(_settingsClone).empty())
865865
{
866-
ShowLoadWarningsDialog.raise(*this, _settingsClone.Warnings());
866+
// The write failed. Surface the clone's existing warnings plus an
867+
// explicit "failed to write" warning (the old WriteSettingsToDisk
868+
// appended this internally).
869+
auto warnings{ winrt::single_threaded_vector<SettingsLoadWarnings>() };
870+
for (auto&& w : _settingsClone.Warnings())
871+
{
872+
warnings.Append(w);
873+
}
874+
warnings.Append(SettingsLoadWarnings::FailedToWriteToSettings);
875+
ShowLoadWarningsDialog.raise(*this, warnings.GetView());
867876
}
868877
}
869878

src/cascadia/TerminalSettingsModel/CascadiaSettings.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
143143

144144
struct CascadiaSettings : CascadiaSettingsT<CascadiaSettings>
145145
{
146+
// JsonManager performs settings.json persistence (GH#12424) and reads the
147+
// cached _currentDefaultTerminal directly to persist the default-terminal
148+
// choice without triggering an expensive defterm refresh.
149+
friend struct JsonManager;
150+
146151
public:
147152
static Model::CascadiaSettings LoadDefaults();
148153
static Model::CascadiaSettings LoadAll();
@@ -168,8 +173,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
168173
Model::ActionMap ActionMap() const noexcept;
169174
winrt::Windows::Foundation::Collections::IVectorView<Model::ExtensionPackage> Extensions();
170175
void ResetApplicationState() const;
171-
void ResetToDefaultSettings();
172-
bool WriteSettingsToDisk();
173176
void SetWriteHandler(std::function<void()> handler);
174177

175178
Json::Value ToJson() const;
@@ -203,16 +206,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
203206
private:
204207
static const std::filesystem::path& _settingsPath();
205208
static const std::filesystem::path& _releaseSettingsPath();
206-
static winrt::hstring _calculateHash(std::string_view settings, const FILETIME& lastWriteTime);
207209

208210
winrt::com_ptr<implementation::Profile> _createNewProfile(const std::wstring_view& name) const;
209211
Model::Profile _getProfileForCommandLine(const winrt::hstring& commandLine) const;
210212
void _refreshDefaultTerminals();
211-
void _writeSettingsToDisk(std::string_view contents);
212213

213-
// Auto-save (GH#12424): the shared "request auto-save" sink installed on
214-
// every inheritable object in this live tree. Empty until SetWriteHandler.
215-
std::shared_ptr<std::function<void()>> _writeSink;
214+
// Auto-save (GH#12424): the shared "request auto-save" notifier installed
215+
// on every inheritable object in this live tree. Empty until SetWriteHandler.
216+
std::shared_ptr<implementation::SettingsWriteNotifier> _writeSink;
216217
void _installWriteSink();
217218

218219
void _resolveDefaultProfile() const;

src/cascadia/TerminalSettingsModel/CascadiaSettings.idl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ namespace Microsoft.Terminal.Settings.Model
3030

3131
CascadiaSettings Copy();
3232
void ResetApplicationState();
33-
void ResetToDefaultSettings();
34-
Boolean WriteSettingsToDisk();
3533
void LogSettingChanges(Boolean isJsonLoad);
3634

3735
String Hash { get; };

src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp

Lines changed: 25 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "ApplicationState.h"
2121
#include "DefaultTerminal.h"
2222
#include "FileUtils.h"
23+
#include "JsonManager.h"
2324

2425
#include "ProfileEntry.h"
2526
#include "FolderEntry.h"
@@ -1284,13 +1285,23 @@ try
12841285
// settings string back to the file.
12851286
if (mustWriteToDisk)
12861287
{
1287-
settings->WriteSettingsToDisk();
1288+
// JsonManager::WriteSettings (GH#12424) force-writes and returns the new
1289+
// hash (empty on failure); capture it so reload-detection has an accurate
1290+
// baseline.
1291+
const auto hash = JsonManager::WriteSettings(settings.as<Model::CascadiaSettings>());
1292+
if (hash.empty())
1293+
{
1294+
settings->_warnings.Append(SettingsLoadWarnings::FailedToWriteToSettings);
1295+
}
1296+
else
1297+
{
1298+
settings->_hash = hash;
1299+
}
12881300
}
12891301
else
12901302
{
12911303
// lastWriteTime is only valid if mustWriteToDisk is false.
1292-
// Additionally WriteSettingsToDisk() updates the _hash for us already.
1293-
settings->_hash = _calculateHash(settingsString, lastWriteTime);
1304+
settings->_hash = CalculateSettingsHash(settingsString, lastWriteTime);
12941305
}
12951306

12961307
settings->_researchOnLoad();
@@ -1496,7 +1507,7 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) :
14961507
// and the defaults fallback never auto-save.
14971508
void CascadiaSettings::_installWriteSink()
14981509
{
1499-
_writeSink = std::make_shared<std::function<void()>>();
1510+
_writeSink = std::make_shared<implementation::SettingsWriteNotifier>();
15001511

15011512
const auto installForProfile = [&](implementation::Profile* prof) {
15021513
if (!prof)
@@ -1532,13 +1543,22 @@ void CascadiaSettings::_installWriteSink()
15321543
{
15331544
installForProfile(winrt::get_self<implementation::Profile>(p));
15341545
}
1546+
1547+
// TODO CARLOS: Known gaps (GH#12424): ColorScheme and Theme are not IInheritable / not
1548+
// JSON-backed, so per-property in-place edits (e.g. SetColorTableEntry,
1549+
// individual theme colors) do NOT trigger auto-save. ActionMap is IInheritable
1550+
// but not JSON-backed, so individual action edits don't either. Add/remove of
1551+
// color schemes and themes IS covered via the GlobalAppSettings collection
1552+
// mutators (AddColorScheme/RemoveColorScheme/DuplicateColorScheme/AddTheme).
1553+
// Full per-property coverage waits for those types becoming JSON-backed and is
1554+
// only required once the settings editor relies on auto-save.
15351555
}
15361556

15371557
void CascadiaSettings::SetWriteHandler(std::function<void()> handler)
15381558
{
15391559
if (_writeSink)
15401560
{
1541-
*_writeSink = std::move(handler);
1561+
_writeSink->SetHandler(std::move(handler));
15421562
}
15431563
}
15441564

@@ -1566,12 +1586,6 @@ const std::filesystem::path& CascadiaSettings::_releaseSettingsPath()
15661586
return path;
15671587
}
15681588

1569-
// Returns a has (approximately) uniquely identifying the settings.json contents on disk.
1570-
winrt::hstring CascadiaSettings::_calculateHash(std::string_view settings, const FILETIME& lastWriteTime)
1571-
{
1572-
return CalculateSettingsHash(settings, lastWriteTime);
1573-
}
1574-
15751589
// This returns something akin to %LOCALAPPDATA%\Packages\WindowsTerminalDev_8wekyb3d8bbwe\LocalState
15761590
// just like SettingsPath(), but without the trailing \settings.json.
15771591
winrt::hstring CascadiaSettings::SettingsDirectory()
@@ -1628,58 +1642,6 @@ void CascadiaSettings::ResetApplicationState() const
16281642
state.Flush();
16291643
}
16301644

1631-
void CascadiaSettings::ResetToDefaultSettings()
1632-
{
1633-
ApplicationState::SharedInstance().Reset();
1634-
_writeSettingsToDisk(LoadStringResource(IDR_USER_DEFAULTS));
1635-
}
1636-
1637-
// Method Description:
1638-
// - Write the current state of CascadiaSettings to our settings file
1639-
// - Create a backup file with the current contents, if one does not exist
1640-
// - Persists the default terminal handler choice to the registry
1641-
// Arguments:
1642-
// - <none>
1643-
// Return Value:
1644-
// - <none>
1645-
bool CascadiaSettings::WriteSettingsToDisk()
1646-
{
1647-
// write current settings to current settings file
1648-
Json::StreamWriterBuilder wbuilder;
1649-
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
1650-
wbuilder.settings_["indentation"] = " ";
1651-
wbuilder.settings_["precision"] = 6; // prevent values like 1.1000000000000001
1652-
1653-
try
1654-
{
1655-
_writeSettingsToDisk(Json::writeString(wbuilder, ToJson()));
1656-
}
1657-
catch (...)
1658-
{
1659-
LOG_CAUGHT_EXCEPTION();
1660-
_warnings.Append(SettingsLoadWarnings::FailedToWriteToSettings);
1661-
return false;
1662-
}
1663-
return true;
1664-
}
1665-
1666-
void CascadiaSettings::_writeSettingsToDisk(std::string_view contents)
1667-
{
1668-
FILETIME lastWriteTime{};
1669-
// Auto-save (GH#12424): write atomically via a process-unique temp file and
1670-
// keep a best-effort "settings.json.bak" backup of the prior contents.
1671-
WriteSettingsFile(_settingsPath(), contents, /*makeBackup*/ true, &lastWriteTime);
1672-
1673-
_hash = _calculateHash(contents, lastWriteTime);
1674-
1675-
// Persists the default terminal choice
1676-
// GH#10003 - Only do this if _currentDefaultTerminal was actually initialized.
1677-
if (_currentDefaultTerminal)
1678-
{
1679-
DefaultTerminal::Current(_currentDefaultTerminal);
1680-
}
1681-
}
1682-
16831645
#ifndef NDEBUG
16841646
[[maybe_unused]] static std::string _getDevPathToSchema()
16851647
{

src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,11 +277,16 @@ void GlobalAppSettings::LayerActionsFrom(const Json::Value& json, const OriginTa
277277
void GlobalAppSettings::AddColorScheme(const Model::ColorScheme& scheme)
278278
{
279279
_colorSchemes.Insert(scheme.Name(), scheme);
280+
// Coarse-grained auto-save coverage (GH#12424): color schemes aren't
281+
// IInheritable, so add/remove is notified here at the collection level. (No-op
282+
// until this tree is the live, bound tree.)
283+
_NotifyWriteSettings();
280284
}
281285

282286
void GlobalAppSettings::RemoveColorScheme(hstring schemeName)
283287
{
284288
_colorSchemes.TryRemove(schemeName);
289+
_NotifyWriteSettings();
285290
}
286291

287292
winrt::Microsoft::Terminal::Settings::Model::ColorScheme GlobalAppSettings::DuplicateColorScheme(const Model::ColorScheme& source)
@@ -455,6 +460,9 @@ winrt::Microsoft::Terminal::Settings::Model::Theme GlobalAppSettings::CurrentThe
455460
void GlobalAppSettings::AddTheme(const Model::Theme& theme)
456461
{
457462
_themes.Insert(theme.Name(), theme);
463+
// Coarse-grained auto-save coverage (GH#12424): themes aren't IInheritable,
464+
// so add is notified here at the collection level. (No-op until bound.)
465+
_NotifyWriteSettings();
458466
}
459467

460468
winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::Theme> GlobalAppSettings::Themes() noexcept

src/cascadia/TerminalSettingsModel/IInheritable.h

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,33 @@ Author(s):
2222

2323
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
2424
{
25+
// A late-bound, shared "request auto-save" notifier (GH#12424). One instance
26+
// is created by CascadiaSettings and installed (by shared_ptr) on every
27+
// object in the *live* settings tree. When a setting is mutated, the object
28+
// calls Notify(); CascadiaSettings binds the actual handler after a
29+
// successful load via SetHandler(). Until a handler is bound, Notify() is a
30+
// no-op. The notifier is deliberately NOT propagated by Copy()/clone paths,
31+
// so editor clones (and the defaults fallback) never auto-save the user's
32+
// settings.json.
33+
struct SettingsWriteNotifier
34+
{
35+
void Notify() const
36+
{
37+
if (_handler)
38+
{
39+
_handler();
40+
}
41+
}
42+
43+
void SetHandler(std::function<void()> handler)
44+
{
45+
_handler = std::move(handler);
46+
}
47+
48+
private:
49+
std::function<void()> _handler;
50+
};
51+
2552
template<typename T>
2653
struct IInheritable
2754
{
@@ -68,12 +95,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
6895
}
6996

7097
// A shared "request auto-save" sink. The owning CascadiaSettings
71-
// creates one sink and installs it on every object in the *live* tree.
72-
// When a setting on this layer is mutated, _NotifyWriteSettings() invokes
73-
// the sink, which requests a debounced auto-save. The sink is intentionally
74-
// NOT copied by Copy()/clone paths, so editor clones (and the defaults
75-
// fallback) never auto-save the user's settings.json.
76-
using WriteSettingsSink = std::shared_ptr<std::function<void()>>;
98+
// creates one notifier and installs it on every object in the *live*
99+
// tree. When a setting on this layer is mutated, _NotifyWriteSettings()
100+
// invokes the notifier, which requests a debounced auto-save. The notifier
101+
// is intentionally NOT copied by Copy()/clone paths, so editor clones (and
102+
// the defaults fallback) never auto-save the user's settings.json.
103+
using WriteSettingsSink = std::shared_ptr<SettingsWriteNotifier>;
77104
void SetWriteSettingsSink(const WriteSettingsSink& sink)
78105
{
79106
_writeSettingsSink = sink;
@@ -88,9 +115,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
88115
// No-op until a sink is installed (i.e. only on the committed live tree).
89116
void _NotifyWriteSettings() const
90117
{
91-
if (_writeSettingsSink && *_writeSettingsSink)
118+
if (_writeSettingsSink)
92119
{
93-
(*_writeSettingsSink)();
120+
_writeSettingsSink->Notify();
94121
}
95122
}
96123

0 commit comments

Comments
 (0)