Skip to content

Commit f915cc0

Browse files
author
BRUNER Patrick
committed
portable mode rework
1 parent f047451 commit f915cc0

22 files changed

Lines changed: 1738 additions & 161 deletions

File tree

src/LogExpert.Configuration/ConfigManager.cs

Lines changed: 335 additions & 13 deletions
Large diffs are not rendered by default.

src/LogExpert.Core/Classes/Filter/FilterStarter.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public FilterStarter (ColumnizerCallback callback, int minThreads)
4545
ThreadCount = Environment.ProcessorCount * 4;
4646
ThreadCount = minThreads;
4747
ThreadPool.GetMinThreads(out _, out var completion);
48-
ThreadPool.SetMinThreads(minThreads, completion);
48+
_ = ThreadPool.SetMinThreads(minThreads, completion);
4949
ThreadPool.GetMaxThreads(out _, out _);
5050
}
5151

@@ -168,14 +168,6 @@ private Filter DoWork (FilterParams filterParams, int startLine, int maxCount, P
168168
return filter;
169169
}
170170

171-
private void FilterDoneCallback (Filter filter)
172-
{
173-
lock (_filterReadyList)
174-
{
175-
_filterReadyList.Add(filter);
176-
}
177-
}
178-
179171
private void MergeResults ()
180172
{
181173
_logger.Info(CultureInfo.InvariantCulture, "Merging filter results.");

src/LogExpert.Core/Classes/Persister/Persister.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using LogExpert.Core.Classes.JsonConverters;
44
using LogExpert.Core.Config;
5+
using LogExpert.Core.Interface;
56

67
using Newtonsoft.Json;
78

@@ -47,13 +48,13 @@ public static class Persister
4748
/// <param name="preferences">The user preferences that determine the save location and other settings. This parameter cannot be <see
4849
/// langword="null"/>.</param>
4950
/// <returns>The full path of the file where the persistence data was saved.</returns>
50-
public static string SavePersistenceData (string logFileName, PersistenceData persistenceData, Preferences preferences, string applicationStartupPath)
51+
public static string SavePersistenceData (string logFileName, PersistenceData persistenceData, Preferences preferences, string sessionBaseDirectory)
5152
{
5253
ArgumentNullException.ThrowIfNull(preferences);
5354
ArgumentNullException.ThrowIfNull(persistenceData);
54-
ArgumentException.ThrowIfNullOrWhiteSpace(applicationStartupPath);
55+
ArgumentException.ThrowIfNullOrWhiteSpace(sessionBaseDirectory);
5556

56-
var fileName = persistenceData.SessionFileName ?? BuildPersisterFileName(logFileName, preferences, applicationStartupPath);
57+
var fileName = persistenceData.SessionFileName ?? BuildPersisterFileName(logFileName, preferences, sessionBaseDirectory);
5758

5859
if (preferences.SaveLocation == SessionSaveLocation.SameDir)
5960
{
@@ -84,12 +85,12 @@ public static string SavePersistenceDataWithFixedName (string persistenceFileNam
8485
/// <param name="logFileName">The name of the log file to load persistence data from. This value cannot be null.</param>
8586
/// <param name="preferences">The preferences used to determine the file path and loading behaviour. This value cannot be null.</param>
8687
/// <returns>The loaded <see cref="PersistenceData"/> object containing the persistence information.</returns>
87-
public static PersistenceData LoadPersistenceData (string logFileName, Preferences preferences, string applicationStartupPath)
88+
public static PersistenceData LoadPersistenceData (string logFileName, Preferences preferences, string sessionBaseDirectory)
8889
{
8990
ArgumentNullException.ThrowIfNull(preferences);
90-
ArgumentNullException.ThrowIfNull(applicationStartupPath);
91+
ArgumentNullException.ThrowIfNull(sessionBaseDirectory);
9192

92-
var fileName = BuildPersisterFileName(logFileName, preferences, applicationStartupPath);
93+
var fileName = BuildPersisterFileName(logFileName, preferences, sessionBaseDirectory);
9394
return LoadInternal(fileName);
9495
}
9596

@@ -99,12 +100,12 @@ public static PersistenceData LoadPersistenceData (string logFileName, Preferenc
99100
/// <param name="logFileName">The name of the log file used to determine the persistence data file.</param>
100101
/// <param name="preferences">The preferences that influence the file name generation. Cannot be <see langword="null"/>.</param>
101102
/// <returns>A <see cref="PersistenceData"/> object containing the loaded data.</returns>
102-
public static PersistenceData LoadPersistenceDataOptionsOnly (string logFileName, Preferences preferences, string applicationStartupPath)
103+
public static PersistenceData LoadPersistenceDataOptionsOnly (string logFileName, Preferences preferences, string sessionBaseDirectory)
103104
{
104105
ArgumentNullException.ThrowIfNull(preferences);
105-
ArgumentNullException.ThrowIfNull(applicationStartupPath);
106+
ArgumentNullException.ThrowIfNull(sessionBaseDirectory);
106107

107-
var fileName = BuildPersisterFileName(logFileName, preferences, applicationStartupPath);
108+
var fileName = BuildPersisterFileName(logFileName, preferences, sessionBaseDirectory);
108109
return LoadInternal(fileName);
109110
}
110111

@@ -138,6 +139,7 @@ public static PersistenceData LoadPersistenceDataFromFixedFile (string persisten
138139
/// <returns>A <see cref="PersistenceData"/> object representing the data loaded from the file.</returns>
139140
public static PersistenceData Load (string fileName)
140141
{
142+
//Dont Call ActiveConfigDir here
141143
return LoadInternal(fileName);
142144
}
143145

@@ -155,7 +157,7 @@ public static PersistenceData Load (string fileName)
155157
/// <param name="preferences">The preferences that determine the save location and directory structure for the persister file.</param>
156158
/// <returns>The full file path of the persister file, including the directory and file name, based on the specified log file
157159
/// name and preferences.</returns>
158-
private static string BuildPersisterFileName (string logFileName, Preferences preferences, string applicationStartupPath)
160+
private static string BuildPersisterFileName (string logFileName, Preferences preferences, string sessionBaseDirectory)
159161
{
160162
string dir;
161163
string file;
@@ -186,7 +188,7 @@ private static string BuildPersisterFileName (string logFileName, Preferences pr
186188
}
187189
case SessionSaveLocation.ApplicationStartupDir:
188190
{
189-
dir = Path.Join(applicationStartupPath, "sessionFiles");
191+
dir = sessionBaseDirectory;
190192
file = dir + Path.DirectorySeparatorChar + BuildSessionFileNameFromPath(logFileName);
191193
break;
192194
}

src/LogExpert.Core/Classes/Persister/ProjectFileValidator.cs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public static ProjectValidationResult ValidateProject (ProjectData projectData,
3030

3131
var result = new ProjectValidationResult();
3232

33+
// Cache drive letters once to avoid repeated expensive DriveInfo.GetDrives() calls
34+
var cachedDriveLetters = GetFixedDriveLetters();
35+
3336
foreach (var fileName in projectData.FileNames)
3437
{
3538
var normalizedPath = NormalizeFilePath(fileName);
@@ -55,7 +58,7 @@ public static ProjectValidationResult ValidateProject (ProjectData projectData,
5558
{
5659
result.MissingFiles.Add(fileName);
5760

58-
var alternativePaths = FindAlternativePaths(fileName, projectData.ProjectFilePath);
61+
var alternativePaths = FindAlternativePaths(fileName, projectData.ProjectFilePath, cachedDriveLetters);
5962
result.PossibleAlternatives[fileName] = alternativePaths;
6063
}
6164
}
@@ -99,6 +102,25 @@ private static bool IsUri (string fileName)
99102
!uri.Scheme.Equals("file", StringComparison.OrdinalIgnoreCase);
100103
}
101104

105+
/// <summary>
106+
/// Gets the list of fixed drive letters that are ready.
107+
/// Extracted to avoid repeated expensive DriveInfo.GetDrives() calls.
108+
/// </summary>
109+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Drive enumeration can fail for various reasons")]
110+
private static List<char> GetFixedDriveLetters ()
111+
{
112+
try
113+
{
114+
return [.. DriveInfo.GetDrives()
115+
.Where(d => d.IsReady && d.DriveType == DriveType.Fixed)
116+
.Select(d => d.Name[0])];
117+
}
118+
catch
119+
{
120+
return [];
121+
}
122+
}
123+
102124
/// <summary>
103125
/// Searches for alternative file paths that may correspond to the specified file name, considering common locations
104126
/// such as the project directory, its subdirectories, the user's Documents/LogExpert folder, alternate drive
@@ -112,9 +134,10 @@ private static bool IsUri (string fileName)
112134
/// whitespace.</param>
113135
/// <param name="projectFilePath">The full path to the project file used as a reference for searching related directories. Can be null or empty if
114136
/// project context is not available.</param>
137+
/// <param name="cachedDriveLetters">Pre-computed list of fixed drive letters to avoid repeated DriveInfo.GetDrives() calls.</param>
115138
/// <returns>A list of strings containing the full paths of files found that match the specified file name in alternative
116139
/// locations. The list will be empty if no matching files are found.</returns>
117-
private static List<string> FindAlternativePaths (string fileName, string projectFilePath)
140+
private static List<string> FindAlternativePaths (string fileName, string projectFilePath, List<char> cachedDriveLetters)
118141
{
119142
var alternatives = new List<string>();
120143

@@ -191,15 +214,10 @@ UnauthorizedAccessException or
191214
{
192215
try
193216
{
194-
var driveLetters = DriveInfo.GetDrives()
195-
.Where(d => d.IsReady && d.DriveType == DriveType.Fixed)
196-
.Select(d => d.Name[0])
197-
.ToList();
198-
199217
var originalDrive = Path.GetPathRoot(fileName)?[0];
200218
var pathWithoutDrive = fileName.Length > 3 ? fileName[3..] : string.Empty;
201219

202-
foreach (var drive in driveLetters.Where(drive => drive != originalDrive && !string.IsNullOrEmpty(pathWithoutDrive)))
220+
foreach (var drive in cachedDriveLetters.Where(drive => drive != originalDrive && !string.IsNullOrEmpty(pathWithoutDrive)))
203221
{
204222
var alternatePath = $"{drive}:\\{pathWithoutDrive}";
205223
if (File.Exists(alternatePath) && !alternatives.Contains(alternatePath))

src/LogExpert.Core/Interface/IConfigManager.cs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,18 @@ public interface IConfigManager
3434
/// Returns the application startup path combined with "portable" subdirectory.
3535
/// When a portableMode.json file exists in this directory, the application runs in portable mode.
3636
/// </remarks>
37+
[Obsolete("Use PortableConfigDir instead. This property is misnamed and may cause confusion. It will be removed in a future version.")]
3738
string PortableModeDir { get; }
3839

40+
/// <summary>
41+
/// Gets the directory path for portable mode settings.
42+
/// </summary>
43+
/// <remarks>
44+
/// Returns the application startup path combined with "portable" subdirectory.
45+
/// When a portableMode.json file exists in this directory, the application runs in portable mode.
46+
/// </remarks>
47+
string PortableConfigDir { get; }
48+
3949
/// <summary>
4050
/// Gets the standard configuration directory path.
4151
/// </summary>
@@ -51,6 +61,20 @@ public interface IConfigManager
5161
/// <value>Returns "portableMode.json"</value>
5262
string PortableModeSettingsFileName { get; }
5363

64+
/// <summary>
65+
/// {ApplicationStartupPath}/configuration/sessions/<br></br>
66+
/// Used for session file storage in portable mode.
67+
/// </summary>
68+
string PortableSessionDir { get; }
69+
70+
/// <summary>
71+
/// Gets the directory path where the current session's data is stored.
72+
/// </summary>
73+
/// <remarks>This property is useful for accessing files or configurations that are specific to the active
74+
/// session. The returned path may vary between sessions and should not be assumed to be persistent across
75+
/// application restarts.</remarks>
76+
string ActiveSessionDir { get; }
77+
5478
/// <summary>
5579
/// Initializes the ConfigManager with application-specific paths and screen information.
5680
/// This method must be called once before accessing Settings or other configuration.
@@ -164,6 +188,26 @@ public interface IConfigManager
164188
/// <remarks>Call this method to remove all entries from the recent files list, typically to reset user
165189
/// history or in response to a privacy-related action. After calling this method, the list of last open files will
166190
/// be empty until new files are opened.</remarks>
167-
168191
void ClearLastOpenFilesList ();
192+
193+
/// <summary>
194+
/// Returns the active configuration directory based on the current mode.
195+
/// In portable mode: {AppDir}/configuration/
196+
/// In normal mode: %APPDATA%/LogExpert/
197+
/// </summary>
198+
string ActiveConfigDir { get; }
199+
200+
/// <summary>
201+
/// Copies configuration files from normal mode location (%APPDATA%/LogExpert/)
202+
/// to the portable configuration directory ({AppDir}/configuration/).
203+
/// Called when portable mode is activated and user confirms copy.
204+
/// </summary>
205+
void CopyConfigToPortable ();
206+
207+
/// <summary>
208+
/// Moves configuration files from the portable directory ({AppDir}/configuration/)
209+
/// back to normal mode locations (%APPDATA%/LogExpert/).
210+
/// Called when portable mode is deactivated and user confirms migration.
211+
/// </summary>
212+
void MoveConfigFromPortable ();
169213
}

0 commit comments

Comments
 (0)