From befea66cf05a6e4b76eb50531678091cdfe66ab1 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 4 Nov 2013 15:02:18 +0100 Subject: [PATCH] Add IPropertyService.LoadExtraProperties + SaveExtraProperties Add 'IncludeHidden' option for IFileSystem.GetFiles() so that it has the same functionality as FileUtility.SearchFiles() --- doc/technotes/Versioning.html | 23 ++-- .../CodeCoverage/Test/Utils/MockFileSystem.cs | 2 +- .../Debugger/Debugger.AddIn/Pads/WatchPad.cs | 23 +--- .../XmlEditor/Test/Utils/MockFileSystem.cs | 2 +- src/Main/Base/Project/Services/IFileSystem.cs | 33 +++++- .../Base/Project/Util/ChrootFileSystem.cs | 4 +- .../Util/EmbeddedResourceFileSystem.cs | 12 ++- src/Main/Base/Project/Workbench/IWorkbench.cs | 10 +- .../Core/Project/Src/AddInTree/CoreStartup.cs | 62 +---------- .../Core/Project/Src/AddInTree/IAddInTree.cs | 2 +- .../PropertyService/IPropertyService.cs | 19 +++- .../Services/PropertyService/Properties.cs | 4 +- .../PropertyService/PropertyServiceImpl.cs | 87 +++++---------- src/Main/SharpDevelop/Parser/ParserService.cs | 6 +- src/Main/SharpDevelop/Project/Solution.cs | 30 ++---- src/Main/SharpDevelop/Sda/CallHelper.cs | 25 +++-- src/Main/SharpDevelop/Services/FileSystem.cs | 6 +- .../SharpDevelop/Services/PropertyService.cs | 101 ++++++++++++++++++ src/Main/SharpDevelop/SharpDevelop.csproj | 1 + .../Templates/TemplateFileDoozer.cs | 4 +- .../SharpDevelop/Templates/TemplateService.cs | 2 +- .../DisplayBinding/DisplayBindingService.cs | 2 +- .../SharpDevelop/Workbench/FileService.cs | 14 +-- .../Workbench/LayoutConfiguration.cs | 4 +- .../Workbench/WorkbenchStartup.cs | 6 +- .../SharpDevelop/Workbench/WpfWorkbench.cs | 6 +- 26 files changed, 265 insertions(+), 225 deletions(-) create mode 100644 src/Main/SharpDevelop/Services/PropertyService.cs diff --git a/doc/technotes/Versioning.html b/doc/technotes/Versioning.html index 6bf3c05bec..30d27a4661 100644 --- a/doc/technotes/Versioning.html +++ b/doc/technotes/Versioning.html @@ -13,15 +13,14 @@

The assemblyInfo.cs files are updated by the tool "UpdateAssemblyInfo". UpdateAssemblyInfo runs as pre-compile target and always sets the number in GlobalAssemblyInfo.cs -to the subversion revision number. That number is displayed +to the number of commits since a hard-coded start commit (const string BaseCommit). That number is displayed in the splash screen, error dialog and about dialog. It is also used as assembly version.

-The revision number is retrieved by running a function in NSvn.Core.dll equivalent to "svn info" -in the "src" folder. -When this doesn't work (e.g. in an exported tree like the source code download on the build server), the -content of the file src/REVISION is used as revision number. +The revision number is retrieved by running "git rev-list" and counting the number of output lines. +When this doesn't work (e.g. in an exported tree like the source code download on the build server; or git not present in PATH), +the content of the file src/REVISION is used as revision number. When even this fails, the revision '0' is used.

Publisher Policy

@@ -39,7 +38,7 @@ would want to reference.

The binding redirects always redirect requests of a version in the range from some past version containing large breaking changes to the current version. -We will not do such changes anymore after the first release candidate of a SharpDevelop version, +We try to avoid such changes after the first release candidate of a SharpDevelop version, so after the release of SharpDevelop x.y RC1, all future SharpDevelop x.y.*.* versions will be in large parts binary compatible.
@@ -51,5 +50,17 @@ compile it against the oldest SharpDevelop version you want your AddIn to run wi An AddIn compiled against 2.1.0.1800 will run with 2.1.0.1801 (and hopefully even with 2.1.23.45678), but an AddIn compiled against 2.1.0.1801 will fail to load in SharpDevelop 2.1.0.1800.

+

+Additionally, your AddIn should include a <Dependency> in its +.addin file to indicate which SharpDevelop version it is intended for. +This way, users trying to install the AddIn in an incompatible SharpDevelop version will be warned about the +incompatibility, instead of getting an obscure error message. +

+
+	<Manifest>
+		<Identity name = "YourAddInName"/>
+		<Dependency addin="SharpDevelop" version="5.0"/>
+	</Manifest>
+
\ No newline at end of file diff --git a/src/AddIns/Analysis/CodeCoverage/Test/Utils/MockFileSystem.cs b/src/AddIns/Analysis/CodeCoverage/Test/Utils/MockFileSystem.cs index 2c373cc91f..50b5ee6c63 100644 --- a/src/AddIns/Analysis/CodeCoverage/Test/Utils/MockFileSystem.cs +++ b/src/AddIns/Analysis/CodeCoverage/Test/Utils/MockFileSystem.cs @@ -63,7 +63,7 @@ namespace ICSharpCode.CodeCoverage.Tests.Utils throw new NotImplementedException(); } - System.Collections.Generic.IEnumerable IReadOnlyFileSystem.GetFiles(DirectoryName directory, string searchPattern, SearchOption searchOption) + System.Collections.Generic.IEnumerable IReadOnlyFileSystem.GetFiles(DirectoryName directory, string searchPattern, DirectorySearchOptions searchOptions) { throw new NotImplementedException(); } diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs index 02f7407a55..10d5684ab2 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs @@ -65,34 +65,13 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads panel.Children.Add(tree); // ProjectService.SolutionLoaded += delegate { LoadNodes(); }; -// ProjectService.SolutionClosing += delegate { SaveNodes(); }; +// SD.ProjectService.CurrentSolution.PreferencesSaving += delegate { SaveNodes(); }; // LoadNodes(); WindowsDebugger.RefreshingPads += RefreshPad; RefreshPad(); } -// void LoadNodes() -// { -// if (ProjectService.OpenSolution != null) { -// var props = ProjectService.OpenSolution.Preferences.NestedProperties("Watches"); -// foreach (var key in props.Keys) { -// this.Items.Add(new TreeNode(props.Get(key, ""), () => null).ToSharpTreeNode()); -// } -// } -// } -// -// void SaveNodes() -// { -// if (ProjectService.OpenSolution != null) { -// var props = new Properties(); -// ProjectService.OpenSolution.Preferences.SetNestedProperties("Watches", props); -// foreach(var node in this.Items.OfType()) { -// props.Set(node.Name, node.EvalEnabled); -// } -// } -// } - public void AddWatch(string expression = null, bool focus = false) { var node = MakeNode(expression); diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockFileSystem.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockFileSystem.cs index 02fa396916..482eb6a625 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockFileSystem.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockFileSystem.cs @@ -36,7 +36,7 @@ namespace XmlEditor.Tests.Utils directoryFiles.Add(DirectoryName.Create(folder), FileName.Create(file)); } - public IEnumerable GetFiles(DirectoryName folder, string extension, SearchOption searchOption = SearchOption.TopDirectoryOnly) + public IEnumerable GetFiles(DirectoryName folder, string extension, DirectorySearchOptions searchOptions = DirectorySearchOptions.None) { searchedFolders.Add(folder); searchedForFileExtensions.Add(extension); diff --git a/src/Main/Base/Project/Services/IFileSystem.cs b/src/Main/Base/Project/Services/IFileSystem.cs index dd96619032..b271864eb9 100644 --- a/src/Main/Base/Project/Services/IFileSystem.cs +++ b/src/Main/Base/Project/Services/IFileSystem.cs @@ -43,7 +43,36 @@ namespace ICSharpCode.SharpDevelop /// TextReader OpenText(FileName fileName); - IEnumerable GetFiles(DirectoryName directory, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly); + /// + /// Retrieves the list of files in the specified directory. + /// + /// The directory to search in. + /// The search pattern used to filter the result list. + /// For example: "*.exe". + /// This method does not use 8.3 patterns; so "*.htm" will not match ".html". + /// + /// Options that influence the search. + /// + /// An enumerable that iterates through the directory contents + /// The directory does not exist / access is denied. + IEnumerable GetFiles(DirectoryName directory, string searchPattern = "*", DirectorySearchOptions searchOptions = DirectorySearchOptions.None); + } + + [Flags] + public enum DirectorySearchOptions + { + /// + /// Search top directory only; skip hidden files. + /// + None = 0, + /// + /// Include hidden files/subdirectories. + /// + IncludeHidden = 1, + /// + /// Perform a recurive search into subdirectories. + /// + IncludeSubdirectories = 2 } public static class FileSystemExtensions @@ -98,7 +127,7 @@ namespace ICSharpCode.SharpDevelop throw new FileNotFoundException(); } - IEnumerable IReadOnlyFileSystem.GetFiles(DirectoryName directory, string searchPattern, SearchOption searchOption) + IEnumerable IReadOnlyFileSystem.GetFiles(DirectoryName directory, string searchPattern, DirectorySearchOptions searchOptions) { return Enumerable.Empty(); } diff --git a/src/Main/Base/Project/Util/ChrootFileSystem.cs b/src/Main/Base/Project/Util/ChrootFileSystem.cs index 667186c9bf..f01baf5381 100644 --- a/src/Main/Base/Project/Util/ChrootFileSystem.cs +++ b/src/Main/Base/Project/Util/ChrootFileSystem.cs @@ -47,9 +47,9 @@ namespace ICSharpCode.SharpDevelop return fileSystem.OpenText(basePath.Combine(fileName)); } - public IEnumerable GetFiles(DirectoryName directory, string searchPattern, SearchOption searchOption) + public IEnumerable GetFiles(DirectoryName directory, string searchPattern, DirectorySearchOptions searchOptions) { - return fileSystem.GetFiles(basePath.Combine(directory), searchPattern, searchOption) + return fileSystem.GetFiles(basePath.Combine(directory), searchPattern, searchOptions) .Select(file => basePath.GetRelativePath(file)); } } diff --git a/src/Main/Base/Project/Util/EmbeddedResourceFileSystem.cs b/src/Main/Base/Project/Util/EmbeddedResourceFileSystem.cs index 1f9d2d3505..12e9438bad 100644 --- a/src/Main/Base/Project/Util/EmbeddedResourceFileSystem.cs +++ b/src/Main/Base/Project/Util/EmbeddedResourceFileSystem.cs @@ -74,10 +74,16 @@ namespace ICSharpCode.SharpDevelop return new StreamReader(OpenRead(fileName)); } - public IEnumerable GetFiles(DirectoryName directory, string searchPattern, SearchOption searchOption) + public IEnumerable GetFiles(DirectoryName directory, string searchPattern, DirectorySearchOptions searchOptions) { - return fileNames.Value.Where(fn => FileUtility.IsBaseDirectory(directory, fn) - && FileUtility.MatchesPattern(fn.GetFileName(), searchPattern)); + return fileNames.Value.Where(delegate(FileName fn) { + if (!FileUtility.MatchesPattern(fn.GetFileName(), searchPattern)) + return false; + if ((searchOptions & DirectorySearchOptions.IncludeSubdirectories) != 0) + return FileUtility.IsBaseDirectory(directory, fn); + else + return directory.Equals(fn.GetParentDirectory()); + }); } } } diff --git a/src/Main/Base/Project/Workbench/IWorkbench.cs b/src/Main/Base/Project/Workbench/IWorkbench.cs index c0d82223d5..175ba615bd 100644 --- a/src/Main/Base/Project/Workbench/IWorkbench.cs +++ b/src/Main/Base/Project/Workbench/IWorkbench.cs @@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop.Workbench /// This is the basic interface to the workspace. /// [SDService("SD.Workbench")] - public interface IWorkbench : IMementoCapable + public interface IWorkbench { /// /// The main window as IWin32Window. @@ -34,14 +34,6 @@ namespace ICSharpCode.SharpDevelop.Workbench /// bool FullScreen { get; set; } - /// - /// The title shown in the title bar. - /// - string Title { - get; - set; - } - /// /// A collection in which all opened view contents (including all secondary view contents) are saved. /// diff --git a/src/Main/Core/Project/Src/AddInTree/CoreStartup.cs b/src/Main/Core/Project/Src/AddInTree/CoreStartup.cs index 23325b3f0b..6ebf00daae 100644 --- a/src/Main/Core/Project/Src/AddInTree/CoreStartup.cs +++ b/src/Main/Core/Project/Src/AddInTree/CoreStartup.cs @@ -32,58 +32,8 @@ namespace ICSharpCode.Core List addInFiles = new List(); List disabledAddIns = new List(); bool externalAddInsConfigured; - string propertiesName; - string configDirectory; - string dataDirectory; - string applicationName; AddInTreeImpl addInTree; - - /// - /// Sets the name used for the properties (only name, without path or extension). - /// Must be set before StartCoreServices() is called. - /// - public string PropertiesName { - get { - return propertiesName; - } - set { - if (value == null || value.Length == 0) - throw new ArgumentNullException("value"); - propertiesName = value; - } - } - - /// - /// Sets the directory name used for the property service. - /// Must be set before StartCoreServices() is called. - /// Use null to use the default path "%ApplicationData%\%ApplicationName%", - /// where %ApplicationData% is the system setting for - /// "c:\documents and settings\username\application data" - /// and %ApplicationName% is the application name you used in the - /// CoreStartup constructor call. - /// - public string ConfigDirectory { - get { - return configDirectory; - } - set { - configDirectory = value; - } - } - - /// - /// Sets the data directory used to load resources. - /// Must be set before StartCoreServices() is called. - /// Use null to use the default path "ApplicationRootPath\data". - /// - public string DataDirectory { - get { - return dataDirectory; - } - set { - dataDirectory = value; - } - } + string applicationName; /// /// Creates a new CoreStartup instance. @@ -98,7 +48,6 @@ namespace ICSharpCode.Core if (applicationName == null) throw new ArgumentNullException("applicationName"); this.applicationName = applicationName; - propertiesName = applicationName + "Properties"; } /// @@ -198,16 +147,9 @@ namespace ICSharpCode.Core /// Starts the core services. /// This initializes the PropertyService and ResourceService. /// - public void StartCoreServices() + public void StartCoreServices(IPropertyService propertyService) { - if (configDirectory == null) - configDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - applicationName); var container = ServiceSingleton.GetRequiredService(); - var propertyService = new PropertyServiceImpl( - DirectoryName.Create(configDirectory), - DirectoryName.Create(dataDirectory ?? Path.Combine(FileUtility.ApplicationRootPath, "data")), - propertiesName); var applicationStateInfoService = new ApplicationStateInfoService(); addInTree = new AddInTreeImpl(applicationStateInfoService); diff --git a/src/Main/Core/Project/Src/AddInTree/IAddInTree.cs b/src/Main/Core/Project/Src/AddInTree/IAddInTree.cs index 977db7cd8e..d4e5172c74 100644 --- a/src/Main/Core/Project/Src/AddInTree/IAddInTree.cs +++ b/src/Main/Core/Project/Src/AddInTree/IAddInTree.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Core /// A path in the addin tree. /// A parameter that gets passed into the doozer and condition evaluators. /// If true, throws a - /// if the path is not found. If false, an empty ArrayList is returned when the + /// if the path is not found. If false, an empty list is returned when the /// path is not found. IReadOnlyList BuildItems(string path, object parameter, bool throwOnNotFound = true); diff --git a/src/Main/Core/Project/Src/Services/PropertyService/IPropertyService.cs b/src/Main/Core/Project/Src/Services/PropertyService/IPropertyService.cs index 2a5ceeee05..42b6e66f38 100644 --- a/src/Main/Core/Project/Src/Services/PropertyService/IPropertyService.cs +++ b/src/Main/Core/Project/Src/Services/PropertyService/IPropertyService.cs @@ -17,13 +17,11 @@ namespace ICSharpCode.Core /// /// Gets the configuration directory. (usually "%ApplicationData%\%ApplicationName%") /// - /// DirectoryName ConfigDirectory { get; } /// /// Gets the data directory (usually "ApplicationRootPath\data") /// - /// DirectoryName DataDirectory { get; } /// @@ -56,8 +54,23 @@ namespace ICSharpCode.Core void Remove(string key); /// - /// Saves the properties to disk. + /// Saves the main properties to disk. /// void Save(); + + /// + /// Loads extra properties that are not part of the main properties container. + /// Unlike , multiple calls to + /// will return different instances, as the properties are re-loaded from disk every time. + /// To save the properties, you need to call . + /// + /// Properties container that was loaded; or an empty properties container + /// if no container with the specified key exists. + Properties LoadExtraProperties(string key); + + /// + /// Saves extra properties that are not part of the main properties container. + /// + void SaveExtraProperties(string key, Properties p); } } diff --git a/src/Main/Core/Project/Src/Services/PropertyService/Properties.cs b/src/Main/Core/Project/Src/Services/PropertyService/Properties.cs index 828589c904..2f6e2f9eb3 100644 --- a/src/Main/Core/Project/Src/Services/PropertyService/Properties.cs +++ b/src/Main/Core/Project/Src/Services/PropertyService/Properties.cs @@ -190,7 +190,7 @@ namespace ICSharpCode.Core /// /// Sets a single element in this Properties-container. - /// The element will be serialized using a TypeConverter if possible, or DataContractSerializer otherwise. + /// The element will be serialized using a TypeConverter if possible, or XAML serializer otherwise. /// /// Setting a key to null has the same effect as calling . public void Set(string key, T value) @@ -255,7 +255,7 @@ namespace ICSharpCode.Core /// /// Sets a list of elements in this Properties-container. - /// The elements will be serialized using a TypeConverter if possible, or DataContractSerializer otherwise. + /// The elements will be serialized using a TypeConverter if possible, or XAML serializer otherwise. /// /// Passing null or an empty list as value has the same effect as calling . public void SetList(string key, IEnumerable value) diff --git a/src/Main/Core/Project/Src/Services/PropertyService/PropertyServiceImpl.cs b/src/Main/Core/Project/Src/Services/PropertyService/PropertyServiceImpl.cs index 01529fcc2c..8695615228 100644 --- a/src/Main/Core/Project/Src/Services/PropertyService/PropertyServiceImpl.cs +++ b/src/Main/Core/Project/Src/Services/PropertyService/PropertyServiceImpl.cs @@ -9,17 +9,13 @@ using System.IO; using System.Text; using System.Threading; using System.Xml; +using ICSharpCode.Core; namespace ICSharpCode.Core { - public sealed class PropertyServiceImpl : IPropertyService + public class PropertyServiceImpl : IPropertyService { - string propertyFileName; - - DirectoryName configDirectory; - DirectoryName dataDirectory; - - Properties properties; + readonly Properties properties; /// /// Initializes the service for unit-testing (reset properties to an empty property container). @@ -29,25 +25,23 @@ namespace ICSharpCode.Core { properties = new Properties(); } - - public PropertyServiceImpl(DirectoryName configDirectory, DirectoryName dataDirectory, string propertiesName) + + public PropertyServiceImpl(Properties properties) { - this.configDirectory = configDirectory; - this.dataDirectory = dataDirectory; - this.propertyFileName = propertiesName + ".xml"; - Directory.CreateDirectory(configDirectory); - LoadPropertiesFromStream(FileName.Create(Path.Combine(configDirectory, propertyFileName))); + if (properties == null) + throw new ArgumentNullException("properties"); + this.properties = properties; } - public DirectoryName ConfigDirectory { + public virtual DirectoryName ConfigDirectory { get { - return configDirectory; + throw new NotImplementedException(); } } - public DirectoryName DataDirectory { + public virtual DirectoryName DataDirectory { get { - return dataDirectory; + throw new NotImplementedException(); } } @@ -105,50 +99,6 @@ namespace ICSharpCode.Core properties.Remove(key); } - bool LoadPropertiesFromStream(FileName fileName) - { - if (!File.Exists(fileName)) { - properties = new Properties(); - return false; - } - try { - using (LockPropertyFile()) { - properties = Properties.Load(fileName); - return true; - } - } catch (XmlException ex) { - var msgService = ServiceSingleton.GetRequiredService(); - msgService.ShowError("Error loading properties: " + ex.Message + "\nSettings have been restored to default values."); - } - properties = new Properties(); - return false; - } - - public void Save() - { - if (string.IsNullOrEmpty(configDirectory) || string.IsNullOrEmpty(propertyFileName)) - throw new InvalidOperationException("No file name was specified on service creation"); - - var fileName = FileName.Create(Path.Combine(configDirectory, propertyFileName)); - using (LockPropertyFile()) { - properties.Save(fileName); - } - } - - /// - /// Acquires an exclusive lock on the properties file so that it can be opened safely. - /// - public IDisposable LockPropertyFile() - { - Mutex mutex = new Mutex(false, "PropertyServiceSave-30F32619-F92D-4BC0-BF49-AA18BF4AC313"); - mutex.WaitOne(); - return new CallbackOnDispose( - delegate { - mutex.ReleaseMutex(); - mutex.Close(); - }); - } - public event PropertyChangedEventHandler PropertyChanged { add { properties.PropertyChanged += value; } remove { properties.PropertyChanged -= value; } @@ -157,5 +107,18 @@ namespace ICSharpCode.Core public Properties MainPropertiesContainer { get { return properties; } } + + public virtual void Save() + { + } + + public virtual Properties LoadExtraProperties(string key) + { + return new Properties(); + } + + public virtual void SaveExtraProperties(string key, Properties p) + { + } } } diff --git a/src/Main/SharpDevelop/Parser/ParserService.cs b/src/Main/SharpDevelop/Parser/ParserService.cs index 8320d9cae4..dcff64a1bb 100644 --- a/src/Main/SharpDevelop/Parser/ParserService.cs +++ b/src/Main/SharpDevelop/Parser/ParserService.cs @@ -67,7 +67,7 @@ namespace ICSharpCode.SharpDevelop.Parser SD.MainThread.VerifyAccess(); if (!value.SequenceEqual(taskListTokens)) { taskListTokens = value.ToArray(); - PropertyService.SetList("SharpDevelop.TaskListTokens", taskListTokens); + SD.PropertyService.SetList("SharpDevelop.TaskListTokens", taskListTokens); // TODO: trigger reparse? } } @@ -75,8 +75,8 @@ namespace ICSharpCode.SharpDevelop.Parser static IReadOnlyList LoadTaskListTokens() { - if (PropertyService.Contains("SharpDevelop.TaskListTokens")) - return PropertyService.GetList("SharpDevelop.TaskListTokens").ToArray(); + if (SD.PropertyService.Contains("SharpDevelop.TaskListTokens")) + return SD.PropertyService.GetList("SharpDevelop.TaskListTokens"); else return new string[] { "HACK", "TODO", "UNDONE", "FIXME" }; } diff --git a/src/Main/SharpDevelop/Project/Solution.cs b/src/Main/SharpDevelop/Project/Solution.cs index 02ad87ba6d..a1906dbf4b 100644 --- a/src/Main/SharpDevelop/Project/Solution.cs +++ b/src/Main/SharpDevelop/Project/Solution.cs @@ -200,7 +200,7 @@ namespace ICSharpCode.SharpDevelop.Project get { return globalSections; } } - void OnProjectConfigurationMappingChanged(object sender, EventArgs e) + void OnProjectConfigurationMappingChanged(object sender, EventArgs e) { this.IsDirty = true; } @@ -235,26 +235,18 @@ namespace ICSharpCode.SharpDevelop.Project get { return preferences; } } - static FileName GetPreferenceFileName(string projectFileName) + string GetPreferencesKey() { - string directory = Path.Combine(PropertyService.ConfigDirectory, "preferences"); - return FileName.Create(Path.Combine(directory, - Path.GetFileName(projectFileName) - + "." + projectFileName.ToUpperInvariant().GetStableHashCode().ToString("x") - + ".xml")); + return "solution:" + fileName.ToString().ToUpperInvariant(); } internal void LoadPreferences() { - FileName preferencesFile = GetPreferenceFileName(fileName); - if (FileUtility.IsValidPath(preferencesFile) && File.Exists(preferencesFile)) { - try { - preferences = Properties.Load(preferencesFile); - } catch (IOException) { - } catch (UnauthorizedAccessException) { - } catch (XmlException) { - // ignore errors about inaccessible or malformed files - } + try { + preferences = SD.PropertyService.LoadExtraProperties(GetPreferencesKey()); + } catch (IOException) { + } catch (XmlException) { + // ignore errors about inaccessible or malformed files } // Load active configuration from preferences CreateDefaultConfigurationsIfMissing(); @@ -274,12 +266,10 @@ namespace ICSharpCode.SharpDevelop.Project preferences.Set("ActiveConfiguration.Platform", activeConfiguration.Platform); PreferencesSaving(this, EventArgs.Empty); - FileName preferencesFile = GetPreferenceFileName(fileName); - System.IO.Directory.CreateDirectory(preferencesFile.GetParentDirectory()); try { - preferences.Save(preferencesFile); + SD.PropertyService.SaveExtraProperties(GetPreferencesKey(), preferences); } catch (IOException) { - } catch (UnauthorizedAccessException) { + // ignore errors writing to extra properties } } #endregion diff --git a/src/Main/SharpDevelop/Sda/CallHelper.cs b/src/Main/SharpDevelop/Sda/CallHelper.cs index 4363c812d8..10588dec61 100644 --- a/src/Main/SharpDevelop/Sda/CallHelper.cs +++ b/src/Main/SharpDevelop/Sda/CallHelper.cs @@ -50,17 +50,28 @@ namespace ICSharpCode.SharpDevelop.Sda this.useSharpDevelopErrorHandler = true; ExceptionBox.RegisterExceptionBoxForUnhandledExceptions(); } - startup.ConfigDirectory = properties.ConfigDirectory; - startup.DataDirectory = properties.DataDirectory; + string configDirectory = properties.ConfigDirectory; + string dataDirectory = properties.DataDirectory; + string propertiesName; if (properties.PropertiesName != null) { - startup.PropertiesName = properties.PropertiesName; + propertiesName = properties.PropertiesName; + } else { + propertiesName = properties.ApplicationName + "Properties"; } if (properties.ApplicationRootPath != null) { FileUtility.ApplicationRootPath = properties.ApplicationRootPath; } - startup.StartCoreServices(); + if (configDirectory == null) + configDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + properties.ApplicationName); + var propertyService = new PropertyService( + DirectoryName.Create(configDirectory), + DirectoryName.Create(dataDirectory ?? Path.Combine(FileUtility.ApplicationRootPath, "data")), + propertiesName); + + startup.StartCoreServices(propertyService); Assembly exe = Assembly.Load(properties.ResourceAssemblyName); SD.ResourceService.RegisterNeutralStrings(new ResourceManager("ICSharpCode.SharpDevelop.Resources.StringResources", exe)); SD.ResourceService.RegisterNeutralImages(new ResourceManager("ICSharpCode.SharpDevelop.Resources.BitmapResources", exe)); @@ -80,11 +91,11 @@ namespace ICSharpCode.SharpDevelop.Sda } if (properties.AllowAddInConfigurationAndExternalAddIns) { - startup.ConfigureExternalAddIns(Path.Combine(PropertyService.ConfigDirectory, "AddIns.xml")); + startup.ConfigureExternalAddIns(Path.Combine(configDirectory, "AddIns.xml")); } if (properties.AllowUserAddIns) { - startup.ConfigureUserAddIns(Path.Combine(PropertyService.ConfigDirectory, "AddInInstallTemp"), - Path.Combine(PropertyService.ConfigDirectory, "AddIns")); + startup.ConfigureUserAddIns(Path.Combine(configDirectory, "AddInInstallTemp"), + Path.Combine(configDirectory, "AddIns")); } LoggingService.Info("Loading AddInTree..."); diff --git a/src/Main/SharpDevelop/Services/FileSystem.cs b/src/Main/SharpDevelop/Services/FileSystem.cs index 219bdff219..92d6cbeda3 100644 --- a/src/Main/SharpDevelop/Services/FileSystem.cs +++ b/src/Main/SharpDevelop/Services/FileSystem.cs @@ -69,10 +69,12 @@ namespace ICSharpCode.SharpDevelop } } - public IEnumerable GetFiles(DirectoryName directory, string searchPattern, SearchOption searchOption) + public IEnumerable GetFiles(DirectoryName directory, string searchPattern, DirectorySearchOptions searchOptions) { try { - return Directory.EnumerateFiles(directory, searchPattern, searchOption).Select(FileName.Create); + bool searchSubdirectories = (searchOptions & DirectorySearchOptions.IncludeSubdirectories) != 0; + bool ignoreHidden = (searchOptions & DirectorySearchOptions.IncludeHidden) == 0; + return FileUtility.LazySearchDirectory(directory, searchPattern, searchSubdirectories, ignoreHidden); } catch (UnauthorizedAccessException ex) { throw new IOException(ex.Message, ex); } diff --git a/src/Main/SharpDevelop/Services/PropertyService.cs b/src/Main/SharpDevelop/Services/PropertyService.cs new file mode 100644 index 0000000000..55108e4b7a --- /dev/null +++ b/src/Main/SharpDevelop/Services/PropertyService.cs @@ -0,0 +1,101 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.IO; +using System.Threading; +using System.Xml; +using ICSharpCode.Core; +using ICSharpCode.NRefactory.Utils; + +namespace ICSharpCode.SharpDevelop +{ + class PropertyService : PropertyServiceImpl + { + DirectoryName dataDirectory; + DirectoryName configDirectory; + FileName propertiesFileName; + + public PropertyService(DirectoryName configDirectory, DirectoryName dataDirectory, string propertiesName) + : base(LoadPropertiesFromStream(configDirectory.CombineFile(propertiesName + ".xml"))) + { + this.dataDirectory = dataDirectory; + this.configDirectory = configDirectory; + propertiesFileName = configDirectory.CombineFile(propertiesName + ".xml"); + } + + public override DirectoryName ConfigDirectory { + get { + return configDirectory; + } + } + + public override DirectoryName DataDirectory { + get { + return dataDirectory; + } + } + static Properties LoadPropertiesFromStream(FileName fileName) + { + if (!File.Exists(fileName)) { + return new Properties(); + } + try { + using (LockPropertyFile()) { + return Properties.Load(fileName); + } + } catch (XmlException ex) { + SD.MessageService.ShowError("Error loading properties: " + ex.Message + "\nSettings have been restored to default values."); + } catch (IOException ex) { + SD.MessageService.ShowError("Error loading properties: " + ex.Message + "\nSettings have been restored to default values."); + } + return new Properties(); + } + + public override void Save() + { + using (LockPropertyFile()) { + this.MainPropertiesContainer.Save(propertiesFileName); + } + } + + /// + /// Acquires an exclusive lock on the properties file so that it can be opened safely. + /// + static IDisposable LockPropertyFile() + { + Mutex mutex = new Mutex(false, "PropertyServiceSave-30F32619-F92D-4BC0-BF49-AA18BF4AC313"); + mutex.WaitOne(); + return new CallbackOnDispose( + delegate { + mutex.ReleaseMutex(); + mutex.Close(); + }); + } + + FileName GetExtraFileName(string key) + { + return configDirectory.CombineFile("preferences/" + key.GetStableHashCode().ToString("x8") + ".xml"); + } + + public override Properties LoadExtraProperties(string key) + { + var fileName = GetExtraFileName(key); + using (LockPropertyFile()) { + if (File.Exists(fileName)) + return Properties.Load(fileName); + else + return new Properties(); + } + } + + public override void SaveExtraProperties(string key, Properties p) + { + var fileName = GetExtraFileName(key); + using (LockPropertyFile()) { + Directory.CreateDirectory(fileName.GetParentDirectory()); + p.Save(fileName); + } + } + } +} diff --git a/src/Main/SharpDevelop/SharpDevelop.csproj b/src/Main/SharpDevelop/SharpDevelop.csproj index ea0f0aec54..bfa2d7cb57 100644 --- a/src/Main/SharpDevelop/SharpDevelop.csproj +++ b/src/Main/SharpDevelop/SharpDevelop.csproj @@ -168,6 +168,7 @@ + diff --git a/src/Main/SharpDevelop/Templates/TemplateFileDoozer.cs b/src/Main/SharpDevelop/Templates/TemplateFileDoozer.cs index 1b4365a796..c496072e31 100644 --- a/src/Main/SharpDevelop/Templates/TemplateFileDoozer.cs +++ b/src/Main/SharpDevelop/Templates/TemplateFileDoozer.cs @@ -50,8 +50,8 @@ namespace ICSharpCode.SharpDevelop.Templates { var fileSystem = GetFileSystem(args); var templates = new List(); - var xpt = fileSystem.GetFiles(DirectoryName.Create("."), "*.xpt", SearchOption.AllDirectories); - var xft = fileSystem.GetFiles(DirectoryName.Create("."), "*.xft", SearchOption.AllDirectories); + var xpt = fileSystem.GetFiles(DirectoryName.Create("."), "*.xpt", DirectorySearchOptions.IncludeSubdirectories); + var xft = fileSystem.GetFiles(DirectoryName.Create("."), "*.xft", DirectorySearchOptions.IncludeSubdirectories); foreach (var fileName in xpt.Concat(xft)) { using (var stream = fileSystem.OpenRead(fileName)) { var relFileSystem = new ReadOnlyChrootFileSystem(fileSystem, fileName.GetParentDirectory()); diff --git a/src/Main/SharpDevelop/Templates/TemplateService.cs b/src/Main/SharpDevelop/Templates/TemplateService.cs index 4b99813507..d3655d6245 100644 --- a/src/Main/SharpDevelop/Templates/TemplateService.cs +++ b/src/Main/SharpDevelop/Templates/TemplateService.cs @@ -67,7 +67,7 @@ namespace ICSharpCode.SharpDevelop.Templates IReadOnlyList LoadTextTemplates() { - var dir = PropertyService.DataDirectory.Combine(DirectoryName.Create("options/textlib")); + var dir = SD.PropertyService.DataDirectory.CombineDirectory("options/textlib"); return SD.FileSystem.GetFiles(dir, "*.xml") .Select(file => TextTemplateGroup.Load(file)) .ToList(); diff --git a/src/Main/SharpDevelop/Workbench/DisplayBinding/DisplayBindingService.cs b/src/Main/SharpDevelop/Workbench/DisplayBinding/DisplayBindingService.cs index 698014ba65..7dc985364f 100644 --- a/src/Main/SharpDevelop/Workbench/DisplayBinding/DisplayBindingService.cs +++ b/src/Main/SharpDevelop/Workbench/DisplayBinding/DisplayBindingService.cs @@ -24,7 +24,7 @@ namespace ICSharpCode.SharpDevelop.Workbench public DisplayBindingService() { bindings = AddInTree.BuildItems(displayBindingPath, null, true); - displayBindingServiceProperties = PropertyService.NestedProperties("DisplayBindingService"); + displayBindingServiceProperties = SD.PropertyService.NestedProperties("DisplayBindingService"); foreach (var binding in displayBindingServiceProperties.GetList("ExternalProcesses")) { if (binding != null) { AddExternalProcessDisplayBindingInternal(binding); diff --git a/src/Main/SharpDevelop/Workbench/FileService.cs b/src/Main/SharpDevelop/Workbench/FileService.cs index 0d2c02311e..894f634c65 100644 --- a/src/Main/SharpDevelop/Workbench/FileService.cs +++ b/src/Main/SharpDevelop/Workbench/FileService.cs @@ -43,33 +43,33 @@ namespace ICSharpCode.SharpDevelop.Workbench public IRecentOpen RecentOpen { get { return LazyInitializer.EnsureInitialized( - ref recentOpen, () => new RecentOpen(PropertyService.NestedProperties("RecentOpen"))); + ref recentOpen, () => new RecentOpen(SD.PropertyService.NestedProperties("RecentOpen"))); } } public bool DeleteToRecycleBin { get { - return PropertyService.Get("SharpDevelop.DeleteToRecycleBin", true); + return SD.PropertyService.Get("SharpDevelop.DeleteToRecycleBin", true); } set { - PropertyService.Set("SharpDevelop.DeleteToRecycleBin", value); + SD.PropertyService.Set("SharpDevelop.DeleteToRecycleBin", value); } } public bool SaveUsingTemporaryFile { get { - return PropertyService.Get("SharpDevelop.SaveUsingTemporaryFile", true); + return SD.PropertyService.Get("SharpDevelop.SaveUsingTemporaryFile", true); } set { - PropertyService.Set("SharpDevelop.SaveUsingTemporaryFile", value); + SD.PropertyService.Set("SharpDevelop.SaveUsingTemporaryFile", value); } } #endregion #region DefaultFileEncoding public int DefaultFileEncodingCodePage { - get { return PropertyService.Get("SharpDevelop.DefaultFileEncoding", 65001); } - set { PropertyService.Set("SharpDevelop.DefaultFileEncoding", value); } + get { return SD.PropertyService.Get("SharpDevelop.DefaultFileEncoding", 65001); } + set { SD.PropertyService.Set("SharpDevelop.DefaultFileEncoding", value); } } public Encoding DefaultFileEncoding { diff --git a/src/Main/SharpDevelop/Workbench/LayoutConfiguration.cs b/src/Main/SharpDevelop/Workbench/LayoutConfiguration.cs index 4954265de5..055476f493 100644 --- a/src/Main/SharpDevelop/Workbench/LayoutConfiguration.cs +++ b/src/Main/SharpDevelop/Workbench/LayoutConfiguration.cs @@ -20,7 +20,7 @@ namespace ICSharpCode.SharpDevelop.Workbench /// public static string DataLayoutPath { get { - return Path.Combine(PropertyService.DataDirectory, "layouts"); + return Path.Combine(SD.PropertyService.DataDirectory, "layouts"); } } @@ -29,7 +29,7 @@ namespace ICSharpCode.SharpDevelop.Workbench /// public static string ConfigLayoutPath { get { - return Path.Combine(PropertyService.ConfigDirectory, "layouts"); + return Path.Combine(SD.PropertyService.ConfigDirectory, "layouts"); } } diff --git a/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs b/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs index 797cd92585..ec0a3a5bd1 100644 --- a/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs +++ b/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs @@ -50,7 +50,7 @@ namespace ICSharpCode.SharpDevelop.Workbench Project.CustomToolsService.Initialize(); workbench.Initialize(); - workbench.SetMemento(PropertyService.NestedProperties(workbenchMemento)); + workbench.SetMemento(SD.PropertyService.NestedProperties(workbenchMemento)); workbench.WorkbenchLayout = layout; var dlgMsgService = SD.MessageService as IDialogMessageService; @@ -111,7 +111,7 @@ namespace ICSharpCode.SharpDevelop.Workbench } // load previous solution - if (!didLoadSolutionOrFile && PropertyService.Get("SharpDevelop.LoadPrevProjectOnStartup", false)) { + if (!didLoadSolutionOrFile && SD.PropertyService.Get("SharpDevelop.LoadPrevProjectOnStartup", false)) { if (SD.FileService.RecentOpen.RecentProjects.Count > 0) { SD.ProjectService.OpenSolutionOrProject(SD.FileService.RecentOpen.RecentProjects[0]); didLoadSolutionOrFile = true; @@ -138,7 +138,7 @@ namespace ICSharpCode.SharpDevelop.Workbench // save the workbench memento in the ide properties try { - PropertyService.SetNestedProperties(workbenchMemento, SD.Workbench.CreateMemento()); + SD.PropertyService.SetNestedProperties(workbenchMemento, ((WpfWorkbench)SD.Workbench).CreateMemento()); } catch (Exception e) { MessageService.ShowException(e, "Exception while saving workbench state."); } diff --git a/src/Main/SharpDevelop/Workbench/WpfWorkbench.cs b/src/Main/SharpDevelop/Workbench/WpfWorkbench.cs index 4aa30f9c29..d6cff52d76 100644 --- a/src/Main/SharpDevelop/Workbench/WpfWorkbench.cs +++ b/src/Main/SharpDevelop/Workbench/WpfWorkbench.cs @@ -478,7 +478,7 @@ namespace ICSharpCode.SharpDevelop.Workbench FileName ViewContentMementosFileName { get { if (viewContentMementosFileName == null) { - viewContentMementosFileName = FileName.Create(Path.Combine(PropertyService.ConfigDirectory, "LastViewStates.xml")); + viewContentMementosFileName = SD.PropertyService.ConfigDirectory.CombineFile("LastViewStates.xml"); } return viewContentMementosFileName; } @@ -500,8 +500,8 @@ namespace ICSharpCode.SharpDevelop.Workbench } public static bool LoadDocumentProperties { - get { return PropertyService.Get("SharpDevelop.LoadDocumentProperties", true); } - set { PropertyService.Set("SharpDevelop.LoadDocumentProperties", value); } + get { return SD.PropertyService.Get("SharpDevelop.LoadDocumentProperties", true); } + set { SD.PropertyService.Set("SharpDevelop.LoadDocumentProperties", value); } } ///