Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 3 additions & 7 deletions src/LogExpert.Configuration/ConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,15 +315,11 @@ public void AddToFileHistory (string fileName)
Save(SettingsFlags.FileHistory);
}

public void RemoveFromFileHistory (string fileName)
public void ClearFileHistory ()
{
bool findName (string s) => s.ToUpperInvariant().Equals(fileName.ToUpperInvariant(), StringComparison.Ordinal);

var index = Instance.Settings.FileHistoryList.FindIndex(findName);

if (index != -1)
lock (_loadSaveLock)
{
Instance.Settings.FileHistoryList.RemoveAt(index);
Instance.Settings.FileHistoryList.Clear();
}

Save(SettingsFlags.FileHistory);
Expand Down
5 changes: 2 additions & 3 deletions src/LogExpert.Core/Interfaces/IConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,9 @@ public interface IConfigManager
void AddToFileHistory (string fileName);

/// <summary>
/// Removes the specified file name from the file history list.
/// Removes all entries from the file history list.
/// </summary>
/// <param name="fileName">The name of the file to remove from the file history list. Comparison is case-insensitive.</param>
void RemoveFromFileHistory (string fileName);
void ClearFileHistory ();

/// <summary>
/// Clears the list of recently opened files.
Expand Down
9 changes: 9 additions & 0 deletions src/LogExpert.Resources/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,9 @@ Ein ausgewähltes Tool erscheint in der Iconbar. Alle anderen verfügbaren Tools
<data name="LogTabWindow_UI_ToolStripMenuItem_lastUsedToolStripMenuItem" xml:space="preserve">
<value>Zuletzt benutzt</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_clearFileHistoryToolStripMenuItem" xml:space="preserve">
<value>Zuletzt benutzte löschen</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_exitToolStripMenuItem" xml:space="preserve">
<value>Exit</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,9 @@ Checked tools will appear in the icon bar. All other tools are available in the
<data name="LogTabWindow_UI_ToolStripMenuItem_lastUsedToolStripMenuItem" xml:space="preserve">
<value>Last used</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_clearFileHistoryToolStripMenuItem" xml:space="preserve">
<value>Clear recently used</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_exitToolStripMenuItem" xml:space="preserve">
<value>Exit</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,9 @@
<data name="LogTabWindow_UI_ToolStripMenuItem_lastUsedToolStripMenuItem" xml:space="preserve">
<value>最近使用</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_clearFileHistoryToolStripMenuItem" xml:space="preserve">
<value>清除最近使用</value>
</data>
<data name="LogTabWindow_UI_ToolStripMenuItem_exitToolStripMenuItem" xml:space="preserve">
<value>退出</value>
</data>
Expand Down
113 changes: 113 additions & 0 deletions src/LogExpert.Tests/ConfigManagerTests/ClearFileHistoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using LogExpert.Core.Config;

using Newtonsoft.Json;

using NUnit.Framework;

namespace LogExpert.Tests.ConfigManagerTests;

[TestFixture]
public class ClearFileHistoryTests
{
private string _testDir;
private Configuration.ConfigManager _configManager;
private object? _originalSettings;
private bool _originalIsInitialized;
private string _originalConfigDir;

[SetUp]
public void SetUp ()
{
_configManager = Configuration.ConfigManager.Instance;
_originalIsInitialized = ConfigManagerTestReflection.GetPrivateField(_configManager, "_isInitialized") is true;
_originalSettings = ConfigManagerTestReflection.GetPrivateField(_configManager, "_settings");
_originalConfigDir = _configManager.ConfigDir;

_testDir = Path.Join(Path.GetTempPath(), "LogExpert_ClearHistoryTest_" + Guid.NewGuid().ToString("N"));
_ = Directory.CreateDirectory(_testDir);

ConfigManagerTestReflection.ResetConfigManagerInitialization(_configManager);
_configManager.Initialize(_testDir, new Rectangle(0, 0, 1920, 1080));
_configManager.ConfigDir = _testDir;
}

[TearDown]
public void TearDown ()
{
ConfigManagerTestReflection.SetPrivateField(_configManager, "_isInitialized", _originalIsInitialized);
ConfigManagerTestReflection.SetPrivateField(_configManager, "_settings", _originalSettings);
_configManager.ConfigDir = _originalConfigDir;

try
{
if (Directory.Exists(_testDir))
{
Directory.Delete(_testDir, recursive: true);
}
}
catch (IOException)
{
// Ignore IO errors during cleanup
}
catch (UnauthorizedAccessException)
{
// Ignore access errors during cleanup
}
}

[Test]
public void ClearFileHistory_RemovesAllEntries ()
{
_configManager.AddToFileHistory(@"C:\logs\one.log");
_configManager.AddToFileHistory(@"C:\logs\two.log");
Assert.That(_configManager.Settings.FileHistoryList, Has.Count.EqualTo(2));

_configManager.ClearFileHistory();

Assert.That(_configManager.Settings.FileHistoryList, Is.Empty);
}

[Test]
public void ClearFileHistory_WritesEmptyListToSettingsFile ()
{
_configManager.AddToFileHistory(@"C:\logs\one.log");

_configManager.ClearFileHistory();

var settingsFile = Path.Join(_configManager.ActiveConfigDir, "settings.json");
Assert.That(File.Exists(settingsFile), Is.True);

var saved = JsonConvert.DeserializeObject<Settings>(File.ReadAllText(settingsFile));
Assert.That(saved, Is.Not.Null);
Assert.That(saved.FileHistoryList, Is.Empty);
}

[Test]
public void ClearFileHistory_LeavesOtherHistoryListsUntouched ()
{
_configManager.AddToFileHistory(@"C:\logs\one.log");
_configManager.Settings.LastOpenFilesList.Add(@"C:\logs\one.log");
_configManager.Settings.SearchHistoryList.Add("error");
_configManager.Settings.FilterHistoryList.Add("WARN");
_configManager.Settings.FilterRangeHistoryList.Add("10");
_configManager.Settings.UriHistoryList.Add("https://example.org/one.log");
_configManager.Settings.RegexHistory.ExpressionHistoryList.Add(@"^\d+");
_configManager.Settings.RegexHistory.TesttextHistoryList.Add("42");
_configManager.Settings.ColumnizerHistoryList.Add(new ColumnizerHistoryEntry(@"C:\logs\one.log", "JsonColumnizer"));

_configManager.ClearFileHistory();

Assert.Multiple(() =>
{
Assert.That(_configManager.Settings.FileHistoryList, Is.Empty);
Assert.That(_configManager.Settings.LastOpenFilesList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.SearchHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.FilterHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.FilterRangeHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.UriHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.RegexHistory.ExpressionHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.RegexHistory.TesttextHistoryList, Has.Count.EqualTo(1));
Assert.That(_configManager.Settings.ColumnizerHistoryList, Has.Count.EqualTo(1));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void SetUp ()
_configManager = Configuration.ConfigManager.Instance;

// Reset the singleton's initialization state using reflection
ResetConfigManagerInitialization();
ConfigManagerTestReflection.ResetConfigManagerInitialization(_configManager);

_configManager.Initialize(_testDir, new Rectangle(0, 0, 1920, 1080));
}
Expand Down Expand Up @@ -64,19 +64,6 @@ public void TearDown ()

#region Helper Methods

/// <summary>
/// Resets ConfigManager singleton initialization state via reflection.
/// </summary>
private void ResetConfigManagerInitialization ()
{
var isInitializedField = typeof(Configuration.ConfigManager).GetField("_isInitialized", BindingFlags.NonPublic | BindingFlags.Instance);
isInitializedField?.SetValue(_configManager, false);

// Reset settings so they reload from the new path
var settingsField = typeof(Configuration.ConfigManager).GetField("_settings", BindingFlags.NonPublic | BindingFlags.Instance);
settingsField?.SetValue(_configManager, null);
}

/// <summary>
/// Invokes a private instance method using reflection.
/// </summary>
Expand Down Expand Up @@ -758,7 +745,7 @@ public void Load_NewPortableLayout_DetectedCorrectly ()
CreateSettingsFile(_configManager.PortableConfigDir);

// Reset settings to force reload
ResetConfigManagerInitialization();
ConfigManagerTestReflection.ResetConfigManagerInitialization(_configManager);
_configManager.Initialize(_testDir, new Rectangle(0, 0, 1920, 1080));

// Act - Access Settings to trigger Load()
Expand All @@ -783,7 +770,7 @@ public void Load_OldPortableLayout_TriggersMigration ()
"""{ "Preferences": { "FontName": "OldLayout" }, "FilterList": [] }""");

// Reset settings to force reload
ResetConfigManagerInitialization();
ConfigManagerTestReflection.ResetConfigManagerInitialization(_configManager);
_configManager.Initialize(_testDir, new Rectangle(0, 0, 1920, 1080));

// Act - Access Settings to trigger Load() which should migrate
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Reflection;

using LogExpert.Core.Config;

namespace LogExpert.Tests.ConfigManagerTests;

/// <summary>
/// Shared reflection helpers for fixtures that reset the ConfigManager singleton
/// between tests.
/// </summary>
internal static class ConfigManagerTestReflection
{
/// <summary>
/// Resets the ConfigManager singleton's initialization state so the next
/// Initialize call reloads settings from its own directory.
/// </summary>
internal static void ResetConfigManagerInitialization (Configuration.ConfigManager configManager)
{
SetPrivateField(configManager, "_isInitialized", false);

// Reset settings so they reload from the new path
SetPrivateField(configManager, "_settings", null);
}

internal static object? GetPrivateField (Configuration.ConfigManager configManager, string fieldName)
{
return Field(fieldName).GetValue(configManager);
}

internal static void SetPrivateField (Configuration.ConfigManager configManager, string fieldName, object? value)
{
Field(fieldName).SetValue(configManager, value);
}

private static FieldInfo Field (string fieldName)
{
return typeof(Configuration.ConfigManager).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new InvalidOperationException($"ConfigManager.{fieldName} is gone; update the shared reset helper.");
}
}
39 changes: 38 additions & 1 deletion src/LogExpert.UI.Tests/Services/MenuToolbarControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,45 @@ public void PopulateFileHistory_CreatesMenuItems ()
_controller.PopulateFileHistory(history);

var lastUsed = FindMenuItem("lastUsedToolStripMenuItem");
Assert.That(lastUsed.DropDownItems, Has.Count.EqualTo(2));
Assert.That(lastUsed.DropDownItems, Has.Count.EqualTo(4));
Assert.That(lastUsed.DropDownItems[0].Text, Is.EqualTo(@"C:\log1.txt"));
Assert.That(lastUsed.DropDownItems[2], Is.TypeOf<ToolStripSeparator>());
Assert.That(lastUsed.DropDownItems[3].Name, Is.EqualTo("clearFileHistoryToolStripMenuItem"));
Assert.That(lastUsed.DropDownItems[3].Enabled, Is.True);
}

[Test]
public void PopulateFileHistory_DisablesClearItem_WhenHistoryIsEmpty ()
{
_controller.PopulateFileHistory([]);

var lastUsed = FindMenuItem("lastUsedToolStripMenuItem");
Assert.That(lastUsed.DropDownItems, Has.Count.EqualTo(1));
Assert.That(lastUsed.DropDownItems[0].Name, Is.EqualTo("clearFileHistoryToolStripMenuItem"));
Assert.That(lastUsed.DropDownItems[0].Enabled, Is.False);
}

[Test]
public void ClearHistoryItem_RaisesFileHistoryClearRequested_AndNotFileClicked ()
{
var clearRequested = 0;
string clickedFile = null;
_controller.FileHistoryClearRequested += (_, _) => clearRequested++;
_controller.HistoryItemClicked += (_, e) => clickedFile = e.FileName;

_controller.PopulateFileHistory(["test.log"]);
var lastUsed = FindMenuItem("lastUsedToolStripMenuItem");
var clearItem = lastUsed.DropDownItems[^1];

var args = new ToolStripItemClickedEventArgs(clearItem);
var onItemClicked = typeof(ToolStrip).GetMethod("OnItemClicked", BindingFlags.Instance | BindingFlags.NonPublic);
_ = onItemClicked.Invoke(lastUsed.DropDown, [args]);

Assert.Multiple(() =>
{
Assert.That(clearRequested, Is.EqualTo(1));
Assert.That(clickedFile, Is.Null);
});
}

[Test]
Expand Down
9 changes: 5 additions & 4 deletions src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ private void OnFileOperationServiceFileOpened (object? sender, FileOpenedEventAr
private void InitializeMenuToolbarControllerEvents ()
{
_menuToolbarController.HistoryItemClicked += OnMenuControllerHistoryItemClicked;
_menuToolbarController.HistoryItemRemoveRequested += OnMenuControllerHistoryItemRemoveRequested;
_menuToolbarController.FileHistoryClearRequested += OnMenuControllerFileHistoryClearRequested;
_menuToolbarController.HighlightGroupSelected += OnMenuControllerHighlightGroupSelected;
}

Expand All @@ -244,10 +244,11 @@ private void OnMenuControllerHighlightGroupSelected (object? sender, HighlightGr
CurrentLogWindow?.SetCurrentHighlightGroup(e.GroupName);
}

private void OnMenuControllerHistoryItemRemoveRequested (object? sender, HistoryItemClickedEventArgs e)
private void OnMenuControllerFileHistoryClearRequested (object? sender, EventArgs e)
{
ConfigManager.RemoveFromFileHistory(e.FileName);
FillHistoryMenu();
// ClearFileHistory persists via Save, whose ConfigChanged event reaches
// ApplySettings and rebuilds the history menu from there.
ConfigManager.ClearFileHistory();
}

private void OnMenuControllerHistoryItemClicked (object? sender, HistoryItemClickedEventArgs e)
Expand Down
8 changes: 5 additions & 3 deletions src/LogExpert.UI/Interface/IMenuToolbarController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ void InitializeMenus (MenuStrip mainMenu, ToolStrip buttonToolbar, ToolStrip ext
void UpdateHighlightGroups (IEnumerable<string> groups, string selectedGroup);

/// <summary>
/// Populates file history menu with recent files.
/// Includes right-click removal support.
/// Populates the file history menu with recent files, followed by a
/// separator and a "clear recently used" row that raises
/// <see cref="FileHistoryClearRequested"/> when clicked.
/// The clear row is disabled when the history is empty.
/// </summary>
void PopulateFileHistory (IEnumerable<string> fileHistory);

// Events
event EventHandler<HistoryItemClickedEventArgs> HistoryItemClicked;
event EventHandler<HistoryItemClickedEventArgs> HistoryItemRemoveRequested;
event EventHandler FileHistoryClearRequested;
event EventHandler<HighlightGroupSelectedEventArgs> HighlightGroupSelected;
}
Loading
Loading