diff --git a/samples/XmlDOM/Window1.xaml.cs b/samples/XmlDOM/Window1.xaml.cs index 7c8d5b05d3..c20aa0ddf0 100644 --- a/samples/XmlDOM/Window1.xaml.cs +++ b/samples/XmlDOM/Window1.xaml.cs @@ -37,6 +37,7 @@ namespace XmlDOM } TextMarkerService markerService; + List changes = new List(); protected override void OnInitialized(EventArgs e) { @@ -44,8 +45,11 @@ namespace XmlDOM editor.TextArea.TextView.MouseMove += new MouseEventHandler(editor_TextArea_TextView_MouseMove); - editor.Document.Changed += delegate { textDirty = true; }; - parser = new AXmlParser(editor.Document); + editor.Document.Changed += delegate(object sender, DocumentChangeEventArgs e2) { + textDirty = true; + changes.Add(e2); + }; + parser = new AXmlParser(); DispatcherTimer timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromSeconds(0.5); @@ -75,7 +79,8 @@ namespace XmlDOM AXmlDocument doc; parser.Lock.EnterWriteLock(); try { - doc = parser.Parse(); + doc = parser.Parse(editor.Document.Text, changes); + changes.Clear(); } finally { parser.Lock.ExitWriteLock(); } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs index b9ce6c5074..971d2bebab 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs @@ -5,13 +5,16 @@ // $Revision: 2568 $ // -using ICSharpCode.SharpDevelop.Project; +using ICSharpCode.AvalonEdit.Document; using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Xml; +using ICSharpCode.AvalonEdit.Xml; using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Project; namespace ICSharpCode.XamlBinding { @@ -46,6 +49,42 @@ namespace ICSharpCode.XamlBinding { return false; } + + AXmlParser axmlParser; + ICSharpCode.SharpDevelop.ITextBuffer lastParsedContent; + + /// + /// Parse the given text and enter read lock. + /// No parsing is done if the text is older than seen before. + /// + public IDisposable ParseAndLock(ICSharpCode.SharpDevelop.ITextBuffer fileContent) + { + // Is fileContent newer? + if (lastParsedContent == null || fileContent.Version.CompareAge(lastParsedContent.Version) > 0) { + axmlParser.Lock.EnterWriteLock(); + // Dobuble check, now that we are thread-safe + if (lastParsedContent == null) { + // First parse + axmlParser = new AXmlParser(); + axmlParser.Parse(fileContent.Text, null); + lastParsedContent = fileContent; + } else if (fileContent.Version.CompareAge(lastParsedContent.Version) > 0) { + // Incremental parse + var changes = lastParsedContent.Version.GetChangesTo(fileContent.Version). + Select(c => new DocumentChangeEventArgs(c.Offset, c.RemovedText, c.InsertedText)); + axmlParser.Parse(fileContent.Text, changes); + lastParsedContent = fileContent; + } else { + // fileContent is older - no need to parse + } + axmlParser.Lock.EnterReadLock(); + axmlParser.Lock.ExitWriteLock(); + } else { + // fileContent is older - no need to parse + axmlParser.Lock.EnterReadLock(); + } + return new CallbackOnDispose(() => axmlParser.Lock.ExitReadLock()); + } public ICompilationUnit Parse(IProjectContent projectContent, string fileName, ICSharpCode.SharpDevelop.ITextBuffer fileContent) { diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/ParserTests.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/ParserTests.cs index 38f7010d5d..a291d4c508 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/ParserTests.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/XmlParser/ParserTests.cs @@ -154,7 +154,7 @@ namespace ICSharpCode.AvalonEdit.Xml.Tests string content = testFile.Content; Debug.WriteLine("Testing " + testFile.Name + "..."); - AXmlParser parser = new AXmlParser(content); + AXmlParser parser = new AXmlParser(); bool usingDTD = content.Contains(" public class AXmlParser { - string input; AXmlDocument userDocument; - TextDocument textDocument; internal TrackedSegmentCollection TrackedSegments { get; private set; } - ChangeTrackingCheckpoint lastCheckpoint; - ReaderWriterLockSlim lockObject; /// /// Generate syntax error when seeing enity reference other then the build-in ones @@ -102,12 +98,11 @@ namespace ICSharpCode.AvalonEdit.Xml public bool UknonwEntityReferenceIsError { get; set; } /// Create new parser - public AXmlParser(string input) + public AXmlParser() { - this.input = input; this.UknonwEntityReferenceIsError = true; this.TrackedSegments = new TrackedSegmentCollection(); - this.lockObject = new ReaderWriterLockSlim(); + this.Lock = new ReaderWriterLockSlim(); this.userDocument = new AXmlDocument() { Parser = this }; this.userDocument.Document = this.userDocument; @@ -116,15 +111,6 @@ namespace ICSharpCode.AvalonEdit.Xml this.userDocument.IsCached = false; } - /// - /// Create new parser, but do not parse the text yet. - /// - public AXmlParser(TextDocument textDocument) - : this(textDocument.Text) - { - this.textDocument = textDocument; - } - /// Throws exception if condition is false internal static void Assert(bool condition, string message) { @@ -147,23 +133,19 @@ namespace ICSharpCode.AvalonEdit.Xml System.Diagnostics.Debug.WriteLine(string.Format("XML: " + text, pars)); } - public AXmlDocument Parse() + /// + /// Incrementaly parse the given text. + /// You have to hold the write lock. + /// 'changesSinceLastParse' can be null on the first call. + /// + public AXmlDocument Parse(string input, IEnumerable changesSinceLastParse) { if (!Lock.IsWriteLockHeld) throw new InvalidOperationException("Lock needed!"); - if (textDocument != null) { // incremental parse - ChangeTrackingCheckpoint checkpoint; - input = textDocument.CreateSnapshot(out checkpoint).Text; - - // Use changes to invalidate cache - if (lastCheckpoint != null) { - var changes = lastCheckpoint.GetChangesTo(checkpoint); - if (!changes.Any()) - return userDocument; - this.TrackedSegments.UpdateOffsetsAndInvalidate(changes); - } - lastCheckpoint = checkpoint; + // Use changes to invalidate cache + if (changesSinceLastParse != null) { + this.TrackedSegments.UpdateOffsetsAndInvalidate(changesSinceLastParse); } TagReader tagReader = new TagReader(this, input); @@ -180,8 +162,6 @@ namespace ICSharpCode.AvalonEdit.Xml /// /// Makes calls to Parse() thread-safe. Use Lock everywhere Parse() is called. /// - public ReaderWriterLockSlim Lock { - get { return this.lockObject; } - } + public ReaderWriterLockSlim Lock { get; private set; } } }