From 153c30704a82c8cfe4fe2bb8106907d4e11a10ba Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 25 Aug 2011 12:35:30 +0200 Subject: [PATCH] Reimplement DomPersistence. Get rid of some compiler warnings about obsolete members. --- .../Src/AvalonEditViewContent.cs | 20 ++-- .../AvalonEdit.AddIn/Src/CodeEditor.cs | 2 +- .../Project/Src/Bookmarks/BookmarkManager.cs | 2 +- .../Pad/BookmarkPadToolbarCommands.cs | 2 +- .../Project/Src/Gui/Dialogs/GotoDialog.cs | 2 +- .../ProjectOptions/ApplicationSettings.cs | 2 +- .../Src/Gui/Pads/TaskList/TaskListPad.cs | 2 +- .../ParserService/AssemblyParserService.cs | 111 +++++++++++++++++- .../Services/ParserService/ParserService.cs | 29 +++++ .../Src/CallHelper.cs | 5 +- 10 files changed, 154 insertions(+), 23 deletions(-) diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs index 395c7a8300..c5048f91de 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs @@ -151,7 +151,7 @@ namespace ICSharpCode.AvalonEdit.AddIn } // we set the file name after loading because this will place the fold markers etc. - codeEditor.FileName = FileName.Create(file.FileName); + codeEditor.FileName = file.FileName; BookmarksAttach(); } finally { isLoading = false; @@ -163,7 +163,7 @@ namespace ICSharpCode.AvalonEdit.AddIn base.OnFileNameChanged(file); if (file == PrimaryFile) { FileName oldFileName = codeEditor.FileName; - FileName newFileName = FileName.Create(file.FileName); + FileName newFileName = file.FileName; if (!string.IsNullOrEmpty(oldFileName)) ParserService.ClearParseInformation(oldFileName); @@ -211,25 +211,25 @@ namespace ICSharpCode.AvalonEdit.AddIn void BookmarksAttach() { foreach (SDBookmark bookmark in BookmarkManager.GetBookmarks(codeEditor.FileName)) { - bookmark.Document = codeEditor.DocumentAdapter; + bookmark.Document = codeEditor.Document; codeEditor.IconBarManager.Bookmarks.Add(bookmark); } BookmarkManager.Added += BookmarkManager_Added; BookmarkManager.Removed += BookmarkManager_Removed; - PermanentAnchorService.AttachDocument(codeEditor.FileName, codeEditor.DocumentAdapter); + PermanentAnchorService.AttachDocument(codeEditor.FileName, codeEditor.Document); } void BookmarksDetach() { if (codeEditor.FileName != null) { - PermanentAnchorService.DetachDocument(codeEditor.FileName, codeEditor.DocumentAdapter); + PermanentAnchorService.DetachDocument(codeEditor.FileName, codeEditor.Document); } BookmarkManager.Added -= BookmarkManager_Added; BookmarkManager.Removed -= BookmarkManager_Removed; foreach (SDBookmark bookmark in codeEditor.IconBarManager.Bookmarks.OfType()) { - if (bookmark.Document == codeEditor.DocumentAdapter) { + if (bookmark.Document == codeEditor.Document) { bookmark.Document = null; } } @@ -239,7 +239,7 @@ namespace ICSharpCode.AvalonEdit.AddIn void BookmarkManager_Removed(object sender, BookmarkEventArgs e) { codeEditor.IconBarManager.Bookmarks.Remove(e.Bookmark); - if (e.Bookmark.Document == codeEditor.DocumentAdapter) { + if (e.Bookmark.Document == codeEditor.Document) { e.Bookmark.Document = null; } } @@ -248,13 +248,13 @@ namespace ICSharpCode.AvalonEdit.AddIn { if (FileUtility.IsEqualFileName(this.PrimaryFileName, e.Bookmark.FileName)) { codeEditor.IconBarManager.Bookmarks.Add(e.Bookmark); - e.Bookmark.Document = codeEditor.DocumentAdapter; + e.Bookmark.Document = codeEditor.Document; } } void BookmarksNotifyNameChange(FileName oldFileName, FileName newFileName) { - PermanentAnchorService.RenameDocument(oldFileName, newFileName, codeEditor.DocumentAdapter); + PermanentAnchorService.RenameDocument(oldFileName, newFileName, codeEditor.Document); foreach (SDBookmark bookmark in codeEditor.IconBarManager.Bookmarks.OfType()) { bookmark.FileName = newFileName; @@ -321,7 +321,7 @@ namespace ICSharpCode.AvalonEdit.AddIn public IDocument GetDocumentForFile(OpenedFile file) { if (file == this.PrimaryFile) - return codeEditor.DocumentAdapter; + return codeEditor.Document; else return null; } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs index d5c4f9a61e..4f68a185dc 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs @@ -462,7 +462,7 @@ namespace ICSharpCode.AvalonEdit.AddIn // Immediately parse on enter. // This ensures we have up-to-date CC info about the method boundary when a user // types near the end of a method. - ParserService.ParseAsync(this.FileName, this.DocumentAdapter.CreateSnapshot()); + ParserService.ParseAsync(this.FileName, this.Document.CreateSnapshot()); } } } diff --git a/src/Main/Base/Project/Src/Bookmarks/BookmarkManager.cs b/src/Main/Base/Project/Src/Bookmarks/BookmarkManager.cs index f42cfc4157..54d75d7f29 100644 --- a/src/Main/Base/Project/Src/Bookmarks/BookmarkManager.cs +++ b/src/Main/Base/Project/Src/Bookmarks/BookmarkManager.cs @@ -110,7 +110,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks Predicate canToggle, Func bookmarkFactory) { - foreach (SDBookmark bookmark in GetBookmarks(FileName.Create(editor.FileName))) { + foreach (SDBookmark bookmark in GetBookmarks(editor.FileName)) { if (canToggle(bookmark) && bookmark.LineNumber == line) { BookmarkManager.RemoveMark(bookmark); return; diff --git a/src/Main/Base/Project/Src/Bookmarks/Pad/BookmarkPadToolbarCommands.cs b/src/Main/Base/Project/Src/Bookmarks/Pad/BookmarkPadToolbarCommands.cs index 7da3eac63a..a665d2f839 100644 --- a/src/Main/Base/Project/Src/Bookmarks/Pad/BookmarkPadToolbarCommands.cs +++ b/src/Main/Base/Project/Src/Bookmarks/Pad/BookmarkPadToolbarCommands.cs @@ -24,7 +24,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks // get current mark var mark = item.Mark as SDBookmark; int line = mark.LineNumber; - var fileName = new FileName(mark.FileName); + var fileName = mark.FileName; SDBookmark bookmark; if (item.Mark is BreakpointBookmark) { diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/GotoDialog.cs b/src/Main/Base/Project/Src/Gui/Dialogs/GotoDialog.cs index 4c7193089c..42dc2fba55 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/GotoDialog.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/GotoDialog.cs @@ -241,7 +241,7 @@ namespace ICSharpCode.SharpDevelop.Gui List list = new List(); if (ProjectService.OpenSolution != null) { foreach (IProject project in ProjectService.OpenSolution.Projects) { - IProjectContent projectContent = ParserService.GetProjectContent(project); + IProjectContent projectContent = project.ProjectContent; if (projectContent != null) { foreach (ITypeDefinition c in projectContent.GetAllTypes()) { string className = c.Name; diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/ProjectOptions/ApplicationSettings.cs b/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/ProjectOptions/ApplicationSettings.cs index 7e9bd9f279..92c90ed8b3 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/ProjectOptions/ApplicationSettings.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/ProjectOptions/ApplicationSettings.cs @@ -194,7 +194,7 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels public static IList GetPossibleStartupObjects(IProject project) { List results = new List(); - IProjectContent pc = ParserService.GetProjectContent(project); + IProjectContent pc = project.ProjectContent; if (pc != null) { foreach (ITypeDefinition c in pc.GetTypes()) { foreach (IMethod m in c.Methods) { diff --git a/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPad.cs b/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPad.cs index bdefd7afaf..ded0f819cf 100644 --- a/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPad.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPad.cs @@ -195,7 +195,7 @@ namespace ICSharpCode.SharpDevelop.Gui case 1: return ProjectService.CurrentProject != null && ProjectService.CurrentProject.FindFile(item.FileName) != null; case 2: - return WorkbenchSingleton.Workbench.ActiveViewContent != null && WorkbenchSingleton.Workbench.ActiveViewContent.PrimaryFileName == FileName.Create(item.FileName); + return WorkbenchSingleton.Workbench.ActiveViewContent != null && WorkbenchSingleton.Workbench.ActiveViewContent.PrimaryFileName == item.FileName; case 3: return current != null && itemClass != null && current.Namespace == itemClass.Namespace; case 4: diff --git a/src/Main/Base/Project/Src/Services/ParserService/AssemblyParserService.cs b/src/Main/Base/Project/Src/Services/ParserService/AssemblyParserService.cs index 1a5ec0360b..c614bb422c 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/AssemblyParserService.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/AssemblyParserService.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Reflection; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using System.Xml; @@ -11,6 +13,7 @@ using ICSharpCode.Core; using ICSharpCode.NRefactory.Documentation; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.NRefactory.Utils; using ICSharpCode.SharpDevelop.Project; using Microsoft.Build.Tasks; using Mono.Cecil; @@ -130,10 +133,17 @@ namespace ICSharpCode.SharpDevelop.Parser } #endregion - #region Load Assembly + XML documentation - static IProjectContent LoadAssembly(string fileName, CancellationToken cancellationToken) + #region Load Assembly + static IProjectContent LoadAssembly(FileName fileName, CancellationToken cancellationToken) { + DateTime lastWriteTime = File.GetLastWriteTimeUtc(fileName); + string cacheFileName = GetCacheFileName(fileName); + IProjectContent pc = TryReadFromCache(cacheFileName, lastWriteTime); + if (pc != null) + return pc; + LoggingService.Debug("Loading " + fileName); + cancellationToken.ThrowIfCancellationRequested(); var param = new ReaderParameters(); param.AssemblyResolver = new DummyAssemblyResolver(); AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(fileName, param); @@ -151,9 +161,10 @@ namespace ICSharpCode.SharpDevelop.Parser LoggingService.Warn("Ignoring error while reading xml doc from " + xmlDocFile, ex); } } - l.InterningProvider = new SimpleInterningProvider(); l.CancellationToken = cancellationToken; - return l.LoadAssembly(asm); + pc = l.LoadAssembly(asm); + SaveToCache(cacheFileName, lastWriteTime, pc); + return pc; } // used to prevent Cecil from loading referenced assemblies @@ -179,7 +190,9 @@ namespace ICSharpCode.SharpDevelop.Parser return null; } } + #endregion + #region Lookup XML documentation static readonly string referenceAssembliesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"Reference Assemblies\Microsoft\\Framework"); static readonly string frameworkPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"Microsoft.NET\Framework"); @@ -214,6 +227,96 @@ namespace ICSharpCode.SharpDevelop.Parser } #endregion + #region DomPersistence + /// + /// Gets/Sets the directory for cached project contents. + /// + public static string DomPersistencePath { get; set; } + + static string GetCacheFileName(FileName assemblyFileName) + { + if (DomPersistencePath == null) + return null; + string cacheFileName = Path.GetFileNameWithoutExtension(assemblyFileName); + if (cacheFileName.Length > 32) + cacheFileName = cacheFileName.Substring(cacheFileName.Length - 32); // use 32 last characters + cacheFileName = Path.Combine(DomPersistencePath, cacheFileName + "." + assemblyFileName.GetHashCode().ToString("x8") + ".dat"); + return cacheFileName; + } + + static IProjectContent TryReadFromCache(string cacheFileName, DateTime lastWriteTime) + { + if (cacheFileName == null || !File.Exists(cacheFileName)) + return null; + LoggingService.Debug("Deserializing " + cacheFileName); + try { + using (FileStream fs = new FileStream(cacheFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, 4096, FileOptions.SequentialScan)) { + using (BinaryReader reader = new BinaryReaderWith7BitEncodedInts(fs)) { + if (reader.ReadInt64() != lastWriteTime.Ticks) { + LoggingService.Debug("Timestamp mismatch, deserialization aborted."); + return null; + } + FastSerializer s = new FastSerializer(); + s.SerializationBinder = new MySerializationBinder(); + return (IProjectContent)s.Deserialize(reader); + } + } + } catch (IOException ex) { + LoggingService.Warn(ex); + return null; + } catch (UnauthorizedAccessException ex) { + LoggingService.Warn(ex); + return null; + } catch (SerializationException ex) { + LoggingService.Warn(ex); + return null; + } + } + + static void SaveToCache(string cacheFileName, DateTime lastWriteTime, IProjectContent pc) + { + if (cacheFileName == null) + return; + LoggingService.Debug("Serializing to " + cacheFileName); + Directory.CreateDirectory(DomPersistencePath); + using (FileStream fs = new FileStream(cacheFileName, FileMode.Create, FileAccess.Write)) { + using (BinaryWriter writer = new BinaryWriterWith7BitEncodedInts(fs)) { + writer.Write(lastWriteTime.Ticks); + FastSerializer s = new FastSerializer(); + s.SerializationBinder = new MySerializationBinder(); + s.Serialize(writer, pc); + } + } + } + + sealed class MySerializationBinder : SerializationBinder + { + public override void BindToName(Type serializedType, out string assemblyName, out string typeName) + { + if (serializedType.Assembly == typeof(IProjectContent).Assembly) { + assemblyName = "NRefactory"; + } else { + assemblyName = serializedType.Assembly.FullName; + } + typeName = serializedType.FullName; + } + + public override Type BindToType(string assemblyName, string typeName) + { + Assembly asm; + switch (assemblyName) { + case "NRefactory": + asm = typeof(IProjectContent).Assembly; + break; + default: + asm = Assembly.Load(assemblyName); + break; + } + return asm.GetType(typeName); + } + } + #endregion + internal static string FindReferenceAssembly(string shortName) { string path = Path.Combine(referenceAssembliesPath, @".NETFramework\v4.0", shortName + ".dll"); diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs index 415988fdc1..83899ea637 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs @@ -1087,5 +1087,34 @@ namespace ICSharpCode.SharpDevelop.Parser LoggingService.Debug("Resolved " + location + " to " + rr); return rr; } + + public static Task ResolveAsync(FileName fileName, TextLocation location, ITextSource fileContent = null, + CancellationToken cancellationToken = default(CancellationToken)) + { + var entry = GetFileEntry(fileName, true); + if (entry.parser == null) + return NullTask(); + return entry.ParseAsync(fileContent).ContinueWith( + delegate (Task parseInfoTask) { + var parseInfo = parseInfoTask.Result; + if (parseInfo == null) + return null; + IProject project = GetProject(parseInfo.ProjectContent); + var context = project != null ? project.TypeResolveContext : GetDefaultTypeResolveContext(); + ResolveResult rr; + using (var ctx = context.Synchronize()) { + rr = entry.parser.Resolve(parseInfo, location, ctx, cancellationToken); + } + LoggingService.Debug("Resolved " + location + " to " + rr); + return rr; + }, cancellationToken); + } + + static Task NullTask() where T : class + { + TaskCompletionSource tcs = new TaskCompletionSource(); + tcs.SetResult(null); + return tcs.Task; + } } } diff --git a/src/Main/ICSharpCode.SharpDevelop.Sda/Src/CallHelper.cs b/src/Main/ICSharpCode.SharpDevelop.Sda/Src/CallHelper.cs index 3f3dbf5a74..d7a03938d9 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Sda/Src/CallHelper.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Sda/Src/CallHelper.cs @@ -12,11 +12,11 @@ using System.Resources; using System.Threading; using System.Windows; using System.Windows.Threading; - using ICSharpCode.Core; using ICSharpCode.Core.WinForms; using ICSharpCode.SharpDevelop.Commands; using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Parser; namespace ICSharpCode.SharpDevelop.Sda { @@ -49,8 +49,7 @@ namespace ICSharpCode.SharpDevelop.Sda if (properties.PropertiesName != null) { startup.PropertiesName = properties.PropertiesName; } - #warning reimplement DOM persistence, or remove the setting - //AssemblyParserService.DomPersistencePath = properties.DomPersistencePath; + AssemblyParserService.DomPersistencePath = properties.DomPersistencePath; if (properties.ApplicationRootPath != null) { FileUtility.ApplicationRootPath = properties.ApplicationRootPath;