Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion Terminal.Gui/Configuration/ConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ internal static void Initialize ()
// Some getters (notably ThemeManager.Themes while uninitialized) synthesize composite
// objects from other configuration properties, so a single populate-and-clone pass can
// observe half-initialized cache entries and persist null/default values into the clone.
hardCodedProperty.Value.PropertyValue = DeepCloner.DeepClone (hardCodedProperty.Value.PropertyValue);
hardCodedProperty.Value.PropertyValue = CloneHardCodedPropertyValue (hardCodedProperty.Value.PropertyValue);
hardCodedProperty.Value.Immutable = true;
}
}
Expand All @@ -222,6 +222,56 @@ internal static void Initialize ()
ThemeManager.Themes? [ThemeManager.Theme].Apply ();
}

private static object? CloneHardCodedPropertyValue (object? propertyValue)
{
if (propertyValue is Dictionary<Command, PlatformKeyBinding> keyBindings)
{
return CloneKeyBindings (keyBindings);
}

return DeepCloner.DeepClone (propertyValue);
}

private static Dictionary<Command, PlatformKeyBinding> CloneKeyBindings (Dictionary<Command, PlatformKeyBinding> source)
{
Dictionary<Command, PlatformKeyBinding> clone = new (source.Comparer);

foreach (KeyValuePair<Command, PlatformKeyBinding> kvp in source)
{
clone [kvp.Key] = ClonePlatformKeyBinding (kvp.Value);
}

return clone;
}

private static PlatformKeyBinding ClonePlatformKeyBinding (PlatformKeyBinding binding)
{
return new ()
{
All = CloneKeyArray (binding.All),
Windows = CloneKeyArray (binding.Windows),
Linux = CloneKeyArray (binding.Linux),
Macos = CloneKeyArray (binding.Macos)
};
}
Comment thread
tig marked this conversation as resolved.

private static Key []? CloneKeyArray (Key []? keys)
{
if (keys is null)
{
return null;
}

Key [] clonedKeys = new Key [keys.Length];

for (var i = 0; i < keys.Length; i++)
{
clonedKeys [i] = new (keys [i]);
}

return clonedKeys;
}

#endregion Initialization

#region Enable/Disable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,60 @@ public void HardCodedDefaultCache_Properties_Are_Copies ()
Disable (true);
}

[Fact]
public void HardCodedDefaultCache_KeyBindingDictionaries_Are_Typed_Deep_Copies ()
{
// Copilot
AssertKeyBindingDictionaryIsDeepCopy ("Application.DefaultKeyBindings", Application.DefaultKeyBindings!);
AssertKeyBindingDictionaryIsDeepCopy ("View.DefaultKeyBindings", View.DefaultKeyBindings!);

Comment thread
tig marked this conversation as resolved.
Outdated
static void AssertKeyBindingDictionaryIsDeepCopy (string propertyName, Dictionary<Command, PlatformKeyBinding> currentBindings)
{
FrozenDictionary<string, ConfigProperty> cache = GetHardCodedConfigPropertyCache ();
Dictionary<Command, PlatformKeyBinding> cachedBindings = Assert.IsType<Dictionary<Command, PlatformKeyBinding>> (cache [propertyName].PropertyValue);

Assert.NotSame (currentBindings, cachedBindings);
Assert.NotEmpty (cachedBindings);

KeyValuePair<Command, PlatformKeyBinding> cachedEntry = cachedBindings.First (kvp => HasAnyKeys (kvp.Value));
PlatformKeyBinding currentBinding = currentBindings [cachedEntry.Key];

Assert.NotSame (currentBinding, cachedEntry.Value);
AssertKeyArraysAreDeepCopies (currentBinding.All, cachedEntry.Value.All);
AssertKeyArraysAreDeepCopies (currentBinding.Windows, cachedEntry.Value.Windows);
AssertKeyArraysAreDeepCopies (currentBinding.Linux, cachedEntry.Value.Linux);
AssertKeyArraysAreDeepCopies (currentBinding.Macos, cachedEntry.Value.Macos);
}

static bool HasAnyKeys (PlatformKeyBinding binding)
{
return binding.All is { Length: > 0 }
|| binding.Windows is { Length: > 0 }
|| binding.Linux is { Length: > 0 }
|| binding.Macos is { Length: > 0 };
}

static void AssertKeyArraysAreDeepCopies (Key [] currentKeys, Key [] cachedKeys)
{
if (currentKeys is null)
{
Assert.Null (cachedKeys);

return;
}
Comment thread
tig marked this conversation as resolved.
Outdated

Assert.NotNull (cachedKeys);
Assert.NotSame (currentKeys, cachedKeys);
Assert.Equal (currentKeys.Length, cachedKeys.Length);

for (var i = 0; i < currentKeys.Length; i++)
{
Assert.NotSame (currentKeys [i], cachedKeys [i]);
Assert.Equal (currentKeys [i], cachedKeys [i]);
}
}
}

[Fact]
public void HardCoded_Default_Theme_Uses_Fully_Populated_Cache_Values ()
{
Expand Down
Loading