diff --git a/src/Main/Base/Project/Src/Dom/XmlDoc.cs b/src/Main/Base/Project/Src/Dom/XmlDoc.cs index 1ff672b079..c94523ed89 100644 --- a/src/Main/Base/Project/Src/Dom/XmlDoc.cs +++ b/src/Main/Base/Project/Src/Dom/XmlDoc.cs @@ -8,18 +8,10 @@ namespace ICSharpCode.SharpDevelop.Dom /// /// Description of XmlDoc. /// - public class XmlDoc + public class XmlDoc : IDisposable { Dictionary xmlDescription = new Dictionary(); - - public Dictionary XmlDescription { - get { - return xmlDescription; - } - } - public XmlDoc() - { - } + Dictionary indexDictionary; void ReadMembersSection(XmlTextReader reader) { @@ -41,6 +33,129 @@ namespace ICSharpCode.SharpDevelop.Dom } } + public string GetDocumentation(string key) + { + lock (xmlDescription) { + if (indexDictionary != null) { + if (!indexDictionary.ContainsKey(key)) + return null; + } + if (xmlDescription.ContainsKey(key)) + return xmlDescription[key]; + if (indexDictionary == null) + return null; + return LoadDocumentation(key); + } + } + + #region Save binary files + // FILE FORMAT FOR BINARY DOCUMENTATION + // long magic = 0x4244636f446c6d58 (identifies file type = 'XmlDocDB') + // short version = 1 (file version) + // long fileDate (last change date of xml file in DateTime ticks) + // int indexPointer (points to location where index starts in the file) + // { string docu } (all documentation strings as length-prefixed strings) + // indexPointer points to the start of the following section: + // { + // string key (documentation key as length-prefixed string) + // int index (index where the docu string starts in the file) + // } + + const long magic = 0x4244636f446c6d58; + const short version = 1; + + void Save(string fileName, DateTime fileDate) + { + using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) { + using (BinaryWriter w = new BinaryWriter(fs)) { + w.Write(magic); + w.Write(version); + w.Write(fileDate.Ticks); + + int indexPointerPos = (int)fs.Position; + w.Write(0); // skip 4 bytes + int[] indices = new int[xmlDescription.Count]; + + int i = 0; + foreach (KeyValuePair p in xmlDescription) { + indices[i++] = (int)fs.Position; + w.Write(p.Value.Trim()); + } + + int indexStart = (int)fs.Position; + i = 0; + foreach (KeyValuePair p in xmlDescription) { + w.Write(p.Key); + w.Write(indices[i++]); + } + w.Seek(indexPointerPos, SeekOrigin.Begin); + w.Write(indexStart); + } + } + } + #endregion + + #region Load binary files + BinaryReader loader; + FileStream fs; + Queue keyCacheQueue; + const int cacheLength = 150; // number of strings to cache when working in file-mode + + bool LoadFromBinary(string fileName, DateTime fileDate) + { + indexDictionary = new Dictionary(); + keyCacheQueue = new Queue(cacheLength); + fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + int len = (int)fs.Length; + loader = new BinaryReader(fs); + if (loader.ReadInt64() != magic) { + Console.WriteLine("Wrong magic"); + return false; + } + if (loader.ReadInt16() != version) { + Console.WriteLine("Wrong version"); + return false; + } + if (loader.ReadInt64() != fileDate.Ticks) { + Console.WriteLine("Wrong date"); + return false; + } + fs.Position = loader.ReadInt32(); // go to start of index + while (fs.Position < len) { + string key = loader.ReadString(); + int pos = loader.ReadInt32(); + indexDictionary.Add(key, pos); + } + return true; + } + + string LoadDocumentation(string key) + { + if (keyCacheQueue.Count > cacheLength - 1) { + xmlDescription.Remove(keyCacheQueue.Dequeue()); + } + int pos = indexDictionary[key]; + fs.Position = pos; + string docu = loader.ReadString(); + xmlDescription.Add(key, docu); + keyCacheQueue.Enqueue(docu); + return docu; + } + + public void Dispose() + { + if (loader != null) { + loader.Close(); + fs.Close(); + } + xmlDescription = null; + indexDictionary = null; + keyCacheQueue = null; + loader = null; + fs = null; + } + #endregion + public static XmlDoc Load(TextReader textReader) { XmlDoc newXmlDoc = new XmlDoc(); @@ -58,11 +173,43 @@ namespace ICSharpCode.SharpDevelop.Dom return newXmlDoc; } + static string MakeTempPath() + { + string tempPath = Path.Combine(Path.GetTempPath(), "SharpDevelop/DocumentationCache"); + if (!Directory.Exists(tempPath)) + Directory.CreateDirectory(tempPath); + return tempPath; + } + public static XmlDoc Load(string fileName) { + string cacheName = MakeTempPath() + "/" + Path.GetFileNameWithoutExtension(fileName) + + "." + fileName.GetHashCode().ToString("x") + ".dat"; + XmlDoc doc; + if (File.Exists(cacheName)) { + doc = new XmlDoc(); + if (doc.LoadFromBinary(cacheName, File.GetLastWriteTimeUtc(fileName))) { + return doc; + } else { + doc.Dispose(); + try { + File.Delete(cacheName); + } catch {} + } + } + using (TextReader textReader = File.OpenText(fileName)) { - return Load(textReader); + doc = Load(textReader); + } + + if (doc.xmlDescription.Count > cacheLength * 2) { + DateTime date = File.GetLastWriteTimeUtc(fileName); + doc.Save(cacheName, date); + doc.Dispose(); + doc = new XmlDoc(); + doc.LoadFromBinary(cacheName, date); } + return doc; } } } diff --git a/src/Main/Base/Project/Src/Services/ParserService/CaseSensitiveProjectContent.cs b/src/Main/Base/Project/Src/Services/ParserService/CaseSensitiveProjectContent.cs index 4ae1d1fa6d..e2c9331257 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/CaseSensitiveProjectContent.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/CaseSensitiveProjectContent.cs @@ -94,12 +94,14 @@ namespace ICSharpCode.Core public string GetXmlDocumentation(string memberTag) { - if (xmlDoc.XmlDescription.ContainsKey(memberTag)) { - return xmlDoc.XmlDescription[memberTag]; + string desc = xmlDoc.GetDocumentation(memberTag); + if (desc != null) { + return desc; } foreach (IProjectContent referencedContent in referencedContents) { - if (referencedContent.XmlDoc.XmlDescription.ContainsKey(memberTag)) { - return referencedContent.XmlDoc.XmlDescription[memberTag]; + desc = referencedContent.XmlDoc.GetDocumentation(memberTag); + if (desc != null) { + return desc; } } return null; @@ -263,6 +265,7 @@ namespace ICSharpCode.Core public void Dispose() { + xmlDoc.Dispose(); ProjectService.ReferenceAdded -= OnReferenceAdded; initializing = false; } diff --git a/src/Main/Base/Project/Src/Services/ParserService/ProjectContentRegistry.cs b/src/Main/Base/Project/Src/Services/ParserService/ProjectContentRegistry.cs index 68c1f4d22a..a60f03de0b 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/ProjectContentRegistry.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/ProjectContentRegistry.cs @@ -24,7 +24,7 @@ namespace ICSharpCode.Core { public class ProjectContentRegistry { - static Dictionary contents = new Dictionary(); + static Dictionary contents = new Dictionary(StringComparer.InvariantCultureIgnoreCase); public static IProjectContent GetMscorlibContent() {