diff --git a/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/CollapsingTests.cs b/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/CollapsingTests.cs index c8ae9d1d4..ddb2535e9 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/CollapsingTests.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/CollapsingTests.cs @@ -143,7 +143,8 @@ namespace ICSharpCode.AvalonEdit.Document CheckHeights(); Assert.AreSame(null, sec1.Start); Assert.AreSame(null, sec1.End); - Assert.IsTrue(sec1.IsCollapsed); + // section gets uncollapsed when it is removed + Assert.IsFalse(sec1.IsCollapsed); } void CheckHeights() diff --git a/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj b/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj index ea99e5af4..0b0d6958e 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj +++ b/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj @@ -85,6 +85,7 @@ + @@ -104,4 +105,7 @@ ICSharpCode.AvalonEdit + + + \ No newline at end of file diff --git a/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Search/FindTests.cs b/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Search/FindTests.cs new file mode 100644 index 000000000..ffdde5533 --- /dev/null +++ b/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Search/FindTests.cs @@ -0,0 +1,114 @@ +// 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.Linq; +using ICSharpCode.AvalonEdit.Document; +using NUnit.Framework; + +namespace ICSharpCode.AvalonEdit.Search +{ + [TestFixture] + public class FindTests + { + [Test] + public void SkipWordBorderSimple() + { + var strategy = SearchStrategyFactory.Create("All", false, true, SearchMode.Normal); + var text = new StringTextSource(" FindAllTests "); + var results = strategy.FindAll(text, 0, text.TextLength).ToArray(); + + Assert.IsEmpty(results, "No results should be found!"); + } + + [Test] + public void SkipWordBorder() + { + var strategy = SearchStrategyFactory.Create("AllTests", false, true, SearchMode.Normal); + var text = new StringTextSource("name=\"{FindAllTests}\""); + var results = strategy.FindAll(text, 0, text.TextLength).ToArray(); + + Assert.IsEmpty(results, "No results should be found!"); + } + + [Test] + public void SkipWordBorder2() + { + var strategy = SearchStrategyFactory.Create("AllTests", false, true, SearchMode.Normal); + var text = new StringTextSource("name=\"FindAllTests "); + var results = strategy.FindAll(text, 0, text.TextLength).ToArray(); + + Assert.IsEmpty(results, "No results should be found!"); + } + + [Test] + public void SkipWordBorder3() + { + var strategy = SearchStrategyFactory.Create("// find", false, true, SearchMode.Normal); + var text = new StringTextSource(" // findtest"); + var results = strategy.FindAll(text, 0, text.TextLength).ToArray(); + + Assert.IsEmpty(results, "No results should be found!"); + } + + [Test] + public void WordBorderTest() + { + var strategy = SearchStrategyFactory.Create("// find", false, true, SearchMode.Normal); + var text = new StringTextSource(" // find me"); + var results = strategy.FindAll(text, 0, text.TextLength).ToArray(); + + Assert.AreEqual(1, results.Length, "One result should be found!"); + Assert.AreEqual(" ".Length, results[0].Offset); + Assert.AreEqual("// find".Length, results[0].Length); + } + + [Test] + public void ResultAtStart() + { + var strategy = SearchStrategyFactory.Create("result", false, true, SearchMode.Normal); + var text = new StringTextSource("result // find me"); + var results = strategy.FindAll(text, 0, text.TextLength).ToArray(); + + Assert.AreEqual(1, results.Length, "One result should be found!"); + Assert.AreEqual(0, results[0].Offset); + Assert.AreEqual("result".Length, results[0].Length); + } + + [Test] + public void ResultAtEnd() + { + var strategy = SearchStrategyFactory.Create("me", false, true, SearchMode.Normal); + var text = new StringTextSource("result // find me"); + var results = strategy.FindAll(text, 0, text.TextLength).ToArray(); + + Assert.AreEqual(1, results.Length, "One result should be found!"); + Assert.AreEqual("result // find ".Length, results[0].Offset); + Assert.AreEqual("me".Length, results[0].Length); + } + + [Test] + public void TextWithDots() + { + var strategy = SearchStrategyFactory.Create("Text", false, true, SearchMode.Normal); + var text = new StringTextSource(".Text."); + var results = strategy.FindAll(text, 0, text.TextLength).ToArray(); + + Assert.AreEqual(1, results.Length, "One result should be found!"); + Assert.AreEqual(".".Length, results[0].Offset); + Assert.AreEqual("Text".Length, results[0].Length); + } + + [Test] + public void SimpleTest() + { + var strategy = SearchStrategyFactory.Create("AllTests", false, false, SearchMode.Normal); + var text = new StringTextSource("name=\"FindAllTests "); + var results = strategy.FindAll(text, 0, text.TextLength).ToArray(); + + Assert.AreEqual(1, results.Length, "One result should be found!"); + Assert.AreEqual("name=\"Find".Length, results[0].Offset); + Assert.AreEqual("AllTests".Length, results[0].Length); + } + } +} diff --git a/AvalonEdit/ICSharpCode.AvalonEdit.Tests/WeakReferenceTests.cs b/AvalonEdit/ICSharpCode.AvalonEdit.Tests/WeakReferenceTests.cs index 29ae3be83..2925945ce 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit.Tests/WeakReferenceTests.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit.Tests/WeakReferenceTests.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Windows.Threading; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Rendering; @@ -45,7 +46,6 @@ namespace ICSharpCode.AvalonEdit } [Test] - [Ignore] public void DocumentDoesNotHoldReferenceToTextArea() { TextDocument textDocument = new TextDocument(); @@ -61,7 +61,6 @@ namespace ICSharpCode.AvalonEdit } [Test] - [Ignore] public void DocumentDoesNotHoldReferenceToTextEditor() { TextDocument textDocument = new TextDocument(); @@ -102,9 +101,12 @@ namespace ICSharpCode.AvalonEdit static void GarbageCollect() { - GC.WaitForPendingFinalizers(); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); - GC.WaitForPendingFinalizers(); + for (int i = 0; i < 3; i++) { + GC.WaitForPendingFinalizers(); + GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); + // pump WPF messages so that WeakEventManager can unregister + Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(delegate {})); + } } } } diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs index 4d995f5ee..036e57f59 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretNavigationCommandHandler.cs @@ -73,6 +73,8 @@ namespace ICSharpCode.AvalonEdit.Editing AddBinding(EditingCommands.SelectToDocumentEnd, Ctrl | Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.DocumentEnd)); CommandBindings.Add(new CommandBinding(ApplicationCommands.SelectAll, OnSelectAll)); + + TextAreaDefaultInputHandler.WorkaroundWPFMemoryLeak(InputBindings); } static void OnSelectAll(object target, ExecutedRoutedEventArgs args) diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs index 77d4d5f5b..239a32fdb 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs @@ -75,6 +75,8 @@ namespace ICSharpCode.AvalonEdit.Editing CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertLeadingTabsToSpaces, OnConvertLeadingTabsToSpaces)); CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertLeadingSpacesToTabs, OnConvertLeadingSpacesToTabs)); CommandBindings.Add(new CommandBinding(AvalonEditCommands.IndentSelection, OnIndentSelection)); + + TextAreaDefaultInputHandler.WorkaroundWPFMemoryLeak(InputBindings); } static TextArea GetTextArea(object target) diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs index 3f5b4448d..c74cebb68 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs @@ -806,11 +806,13 @@ namespace ICSharpCode.AvalonEdit.Editing //Debug.WriteLine("TextInput: Text='" + e.Text + "' SystemText='" + e.SystemText + "' ControlText='" + e.ControlText + "'"); base.OnTextInput(e); if (!e.Handled && this.Document != null) { - if (string.IsNullOrEmpty(e.Text) || e.Text == "\x1b") { + if (string.IsNullOrEmpty(e.Text) || e.Text == "\x1b" || e.Text == "\b") { // ASCII 0x1b = ESC. // WPF produces a TextInput event with that old ASCII control char // when Escape is pressed. We'll just ignore it. + // A deadkey followed by backspace causes a textinput event for the BS character. + // Similarly, some shortcuts like Alt+Space produce an empty TextInput event. // We have to ignore those (not handle them) to keep the shortcut working. return; @@ -846,7 +848,7 @@ namespace ICSharpCode.AvalonEdit.Editing throw ThrowUtil.NoDocumentAssigned(); OnTextEntering(e); if (!e.Handled) { - if (e.Text == "\n" || e.Text == "\r\n") + if (e.Text == "\n" || e.Text == "\r" || e.Text == "\r\n") ReplaceSelectionWithNewLine(); else ReplaceSelectionWithText(e.Text); diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs index 2494fc676..24d817524 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextAreaDefaultInputHandlers.cs @@ -2,7 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; -using System.Linq; +using System.Collections.Generic; using System.Windows; using System.Windows.Input; @@ -54,6 +54,16 @@ namespace ICSharpCode.AvalonEdit.Editing return kb; } + internal static void WorkaroundWPFMemoryLeak(List inputBindings) + { + // Work around WPF memory leak: + // KeyBinding retains a reference to whichever UIElement it is used in first. + // Using a dummy element for this purpose ensures that we don't leak + // a real text editor (which a potentially large document). + UIElement dummyElement = new UIElement(); + dummyElement.InputBindings.AddRange(inputBindings); + } + #region Undo / Redo UndoStack GetUndoStack() { diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs index 87ad45e8a..d20785728 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs @@ -68,13 +68,16 @@ namespace ICSharpCode.AvalonEdit.Folding void OnDocumentChanged(DocumentChangeEventArgs e) { foldings.UpdateOffsets(e); - FoldingSection s = foldings.FindFirstSegmentWithStartAfter(e.Offset); - while (s != null && s.StartOffset == e.Offset) { - FoldingSection next = foldings.GetNextSegment(s); - if (s.Length == 0) { - RemoveFolding(s); + int newEndOffset = e.Offset + e.InsertionLength; + // extend end offset to the end of the line (including delimiter) + var endLine = document.GetLineByOffset(newEndOffset); + newEndOffset = endLine.Offset + endLine.TotalLength; + foreach (var affectedFolding in foldings.FindOverlappingSegments(e.Offset, newEndOffset - e.Offset)) { + if (affectedFolding.Length == 0) { + RemoveFolding(affectedFolding); + } else { + affectedFolding.ValidateCollapsedLineSections(); } - s = next; } } #endregion @@ -100,7 +103,7 @@ namespace ICSharpCode.AvalonEdit.Folding throw new ArgumentException(); foreach (FoldingSection fs in foldings) { if (fs.collapsedSections != null) { - CollapsedLineSection[] c = new CollapsedLineSection[textViews.Count]; + var c = new CollapsedLineSection[textViews.Count]; Array.Copy(fs.collapsedSections, 0, c, 0, pos); Array.Copy(fs.collapsedSections, pos + 1, c, pos, c.Length - pos); fs.collapsedSections = c; @@ -108,15 +111,6 @@ namespace ICSharpCode.AvalonEdit.Folding } } - internal CollapsedLineSection[] CollapseLines(DocumentLine start, DocumentLine end) - { - CollapsedLineSection[] c = new CollapsedLineSection[textViews.Count]; - for (int i = 0; i < c.Length; i++) { - c[i] = textViews[i].CollapseLines(start, end); - } - return c; - } - internal void Redraw() { foreach (TextView textView in textViews) @@ -138,6 +132,8 @@ namespace ICSharpCode.AvalonEdit.Folding { if (startOffset >= endOffset) throw new ArgumentException("startOffset must be less than endOffset"); + if (startOffset < 0 || endOffset > document.TextLength) + throw new ArgumentException("Folding must be within document boundary"); FoldingSection fs = new FoldingSection(this, startOffset, endOffset); foldings.Add(fs); Redraw(fs); @@ -254,6 +250,9 @@ namespace ICSharpCode.AvalonEdit.Folding throw new ArgumentException("newFoldings must be sorted by start offset"); previousStartOffset = newFolding.StartOffset; + int startOffset = newFolding.StartOffset.CoerceValue(0, document.TextLength); + int endOffset = newFolding.EndOffset.CoerceValue(0, document.TextLength); + if (newFolding.StartOffset == newFolding.EndOffset) continue; // ignore zero-length foldings diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingSection.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingSection.cs index 7cf4364fe..0d16c9eb8 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingSection.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingSection.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Diagnostics; using System.Text; using System.Windows.Threading; using ICSharpCode.AvalonEdit.Document; @@ -28,15 +29,20 @@ namespace ICSharpCode.AvalonEdit.Folding if (isFolded != value) { isFolded = value; if (value) { + // Create collapsed sections if (manager != null) { DocumentLine startLine = manager.document.GetLineByOffset(StartOffset); DocumentLine endLine = manager.document.GetLineByOffset(EndOffset); if (startLine != endLine) { DocumentLine startLinePlusOne = startLine.NextLine; - collapsedSections = manager.CollapseLines(startLinePlusOne, endLine); + collapsedSections = new CollapsedLineSection[manager.textViews.Count]; + for (int i = 0; i < collapsedSections.Length; i++) { + collapsedSections[i] = manager.textViews[i].CollapseLines(startLinePlusOne, endLine); + } } } } else { + // Destroy collapsed sections RemoveCollapsedLineSection(); } if (manager != null) @@ -45,6 +51,9 @@ namespace ICSharpCode.AvalonEdit.Folding } } + /// + /// Creates new collapsed section when a text view is added to the folding manager. + /// internal CollapsedLineSection CollapseSection(TextView textView) { DocumentLine startLine = manager.document.GetLineByOffset(StartOffset); @@ -56,6 +65,34 @@ namespace ICSharpCode.AvalonEdit.Folding return null; } + internal void ValidateCollapsedLineSections() + { + if (!isFolded) { + RemoveCollapsedLineSection(); + return; + } + DocumentLine startLine = manager.document.GetLineByOffset(StartOffset); + DocumentLine endLine = manager.document.GetLineByOffset(EndOffset); + if (startLine == endLine) { + RemoveCollapsedLineSection(); + } else { + if (collapsedSections == null) + collapsedSections = new CollapsedLineSection[manager.textViews.Count]; + // Validate collapsed line sections + DocumentLine startLinePlusOne = startLine.NextLine; + for (int i = 0; i < collapsedSections.Length; i++) { + var collapsedSection = collapsedSections[i]; + if (collapsedSection == null || collapsedSection.Start != startLinePlusOne || collapsedSection.End != endLine) { + // recreate this collapsed section + Debug.WriteLine("CollapsedLineSection validation - recreate collapsed section from " + startLinePlusOne + " to " + endLine); + if (collapsedSection != null) + collapsedSection.Uncollapse(); + collapsedSections[i] = manager.textViews[i].CollapseLines(startLinePlusOne, endLine); + } + } + } + } + /// /// Gets/Sets the text used to display the collapsed version of the folding section. /// diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Folding/XmlFoldingStrategy.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Folding/XmlFoldingStrategy.cs index 2ade39284..5adaa2e30 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Folding/XmlFoldingStrategy.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Folding/XmlFoldingStrategy.cs @@ -110,7 +110,7 @@ namespace ICSharpCode.AvalonEdit.Folding // into account the "); foldMarkers.Add(new NewFolding(startOffset, endOffset) { Name = foldText } ); diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj index fb4088075..7dd5c6f12 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj +++ b/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj @@ -42,8 +42,9 @@ False Auto - 452984832 + 4194304 AnyCPU + 4096 diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Properties/GlobalAssemblyInfo.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Properties/GlobalAssemblyInfo.cs index 28e1bfe57..3e81bb71a 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Properties/GlobalAssemblyInfo.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Properties/GlobalAssemblyInfo.cs @@ -18,7 +18,7 @@ using System.Reflection; [assembly: AssemblyProduct("SharpDevelop")] [assembly: AssemblyCopyright("2000-2012 AlphaSierraPapa for the SharpDevelop Team")] [assembly: AssemblyVersion(RevisionClass.Major + "." + RevisionClass.Minor + "." + RevisionClass.Build + "." + RevisionClass.Revision)] -[assembly: AssemblyInformationalVersion(RevisionClass.FullVersion + "-49ea0a14")] +[assembly: AssemblyInformationalVersion(RevisionClass.FullVersion + "-ca8a8e28")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", @@ -29,8 +29,8 @@ internal static class RevisionClass public const string Major = "4"; public const string Minor = "2"; public const string Build = "0"; - public const string Revision = "8549"; - public const string VersionName = "beta"; + public const string Revision = "8752"; + public const string VersionName = "Beta 2"; - public const string FullVersion = Major + "." + Minor + "." + Build + ".8549-beta"; + public const string FullVersion = Major + "." + Minor + "." + Build + ".8752-Beta 2"; } diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/CollapsedLineSection.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/CollapsedLineSection.cs index 35e06f0e1..04dcbc1f9 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/CollapsedLineSection.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/CollapsedLineSection.cs @@ -11,9 +11,8 @@ namespace ICSharpCode.AvalonEdit.Rendering /// Represents a collapsed line section. /// Use the Uncollapse() method to uncollapse the section. /// - public sealed class CollapsedLineSection : INotifyPropertyChanged + public sealed class CollapsedLineSection { - bool isCollapsed = true; DocumentLine start, end; HeightTree heightTree; @@ -41,7 +40,7 @@ namespace ICSharpCode.AvalonEdit.Rendering /// This property initially is true and turns to false when uncollapsing the section. /// public bool IsCollapsed { - get { return isCollapsed; } + get { return start != null; } } /// @@ -51,10 +50,7 @@ namespace ICSharpCode.AvalonEdit.Rendering /// public DocumentLine Start { get { return start; } - internal set { - start = value; - // TODO: raised property changed event (but only after the operation is complete) - } + internal set { start = value; } } /// @@ -64,46 +60,26 @@ namespace ICSharpCode.AvalonEdit.Rendering /// public DocumentLine End { get { return end; } - internal set { - end = value; - // TODO: raised property changed event (but only after the operation is complete) - } + internal set { end = value; } } /// /// Uncollapses the section. /// This causes the Start and End properties to be set to null! - /// Runtime: O(log(n)) + /// Does nothing if the section is already uncollapsed. /// - /// - /// The section is already uncollapsed, or the text containing the section was deleted. - /// public void Uncollapse() { if (start == null) - throw new InvalidOperationException(); + return; heightTree.Uncollapse(this); #if DEBUG heightTree.CheckProperties(); #endif - start = end = null; - isCollapsed = false; - NotifyPropertyChanged("Start"); - NotifyPropertyChanged("End"); - NotifyPropertyChanged("IsCollapsed"); - } - - /// - /// Is raised when of the properties Start,End,IsCollapsed changes. - /// - public event PropertyChangedEventHandler PropertyChanged; - - void NotifyPropertyChanged(string propertyName) - { - if (PropertyChanged != null) - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + start = null; + end = null; } /// @@ -113,7 +89,7 @@ namespace ICSharpCode.AvalonEdit.Rendering public override string ToString() { return "[CollapsedSection" + ID + " Start=" + (start != null ? start.LineNumber.ToString() : "null") - + " End=" + (end != null ? end.LineNumber.ToString() : "null") + " IsCollapsed=" + isCollapsed + "]"; + + " End=" + (end != null ? end.LineNumber.ToString() : "null") + "]"; } } } diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs index 920f9f895..e1ec32c7f 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs @@ -999,12 +999,18 @@ namespace ICSharpCode.AvalonEdit.Rendering visualLine.ConstructVisualElements(textSource, elementGeneratorsArray); - #if DEBUG - for (int i = visualLine.FirstDocumentLine.LineNumber + 1; i <= visualLine.LastDocumentLine.LineNumber; i++) { - if (!heightTree.GetIsCollapsed(i)) - throw new InvalidOperationException("Line " + i + " was skipped by a VisualLineElementGenerator, but it is not collapsed."); + if (visualLine.FirstDocumentLine != visualLine.LastDocumentLine) { + // Check whether the lines are collapsed correctly: + double firstLinePos = heightTree.GetVisualPosition(visualLine.FirstDocumentLine.NextLine); + double lastLinePos = heightTree.GetVisualPosition(visualLine.LastDocumentLine); + if (!firstLinePos.IsClose(lastLinePos)) { + for (int i = visualLine.FirstDocumentLine.LineNumber + 1; i <= visualLine.LastDocumentLine.LineNumber; i++) { + if (!heightTree.GetIsCollapsed(i)) + throw new InvalidOperationException("Line " + i + " was skipped by a VisualLineElementGenerator, but it is not collapsed."); + } + throw new InvalidOperationException("All lines collapsed but visual pos different - height tree inconsistency?"); + } } - #endif visualLine.RunTransformers(textSource, lineTransformersArray); diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs index db7e62747..8e2d67fdd 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs @@ -151,13 +151,13 @@ namespace ICSharpCode.AvalonEdit.Rendering offset += element.DocumentLength; if (offset > currentLineEnd) { DocumentLine newEndLine = document.GetLineByOffset(offset); - if (newEndLine == this.LastDocumentLine) { + currentLineEnd = newEndLine.Offset + newEndLine.Length; + this.LastDocumentLine = newEndLine; + if (currentLineEnd < offset) { throw new InvalidOperationException( "The VisualLineElementGenerator " + g.GetType().Name + " produced an element which ends within the line delimiter"); } - currentLineEnd = newEndLine.Offset + newEndLine.Length; - this.LastDocumentLine = newEndLine; } break; } @@ -179,7 +179,7 @@ namespace ICSharpCode.AvalonEdit.Rendering textOffset += element.DocumentLength; } VisualLength = visualOffset; - Debug.Assert(textOffset == LastDocumentLine.Offset + LastDocumentLine.Length - FirstDocumentLine.Offset); + Debug.Assert(textOffset == LastDocumentLine.EndOffset - FirstDocumentLine.Offset); } internal void RunTransformers(ITextRunConstructionContext context, IVisualLineTransformer[] transformers) diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Search/RegexSearchStrategy.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Search/RegexSearchStrategy.cs index a2535ac39..ad71b7ad2 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Search/RegexSearchStrategy.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Search/RegexSearchStrategy.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; +using System.Windows.Documents; using ICSharpCode.AvalonEdit.Document; @@ -14,23 +15,34 @@ namespace ICSharpCode.AvalonEdit.Search class RegexSearchStrategy : ISearchStrategy { readonly Regex searchPattern; + readonly bool matchWholeWords; - public RegexSearchStrategy(Regex searchPattern) + public RegexSearchStrategy(Regex searchPattern, bool matchWholeWords) { if (searchPattern == null) throw new ArgumentNullException("searchPattern"); this.searchPattern = searchPattern; + this.matchWholeWords = matchWholeWords; } public IEnumerable FindAll(ITextSource document, int offset, int length) { int endOffset = offset + length; foreach (Match result in searchPattern.Matches(document.Text)) { - if (offset <= result.Index && endOffset >= (result.Length + result.Index)) - yield return new SearchResult { StartOffset = result.Index, Length = result.Length, Data = result }; + int resultEndOffset = result.Length + result.Index; + if (offset > result.Index || endOffset < resultEndOffset) + continue; + if (matchWholeWords && (!IsWordBorder(document, result.Index) || !IsWordBorder(document, resultEndOffset))) + continue; + yield return new SearchResult { StartOffset = result.Index, Length = result.Length, Data = result }; } } + static bool IsWordBorder(ITextSource document, int offset) + { + return TextUtilities.GetNextCaretPosition(document, offset - 1, LogicalDirection.Forward, CaretPositioningMode.WordBorder) == offset; + } + public ISearchResult FindNext(ITextSource document, int offset, int length) { return FindAll(document, offset, length).FirstOrDefault(); diff --git a/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchStrategyFactory.cs b/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchStrategyFactory.cs index 25c4661a6..2156ba8e6 100644 --- a/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchStrategyFactory.cs +++ b/AvalonEdit/ICSharpCode.AvalonEdit/Search/SearchStrategyFactory.cs @@ -33,12 +33,9 @@ namespace ICSharpCode.AvalonEdit.Search searchPattern = ConvertWildcardsToRegex(searchPattern); break; } - - if (matchWholeWords) - searchPattern = "\\b" + searchPattern + "\\b"; try { Regex pattern = new Regex(searchPattern, options); - return new RegexSearchStrategy(pattern); + return new RegexSearchStrategy(pattern, matchWholeWords); } catch (ArgumentException ex) { throw new SearchPatternException(ex.Message, ex); } diff --git a/ILSpy/Properties/app.config.template b/ILSpy/Properties/app.config.template index aa82408ee..bd6a5bc90 100644 --- a/ILSpy/Properties/app.config.template +++ b/ILSpy/Properties/app.config.template @@ -9,11 +9,11 @@ - + - + diff --git a/SharpTreeView/EditTextBox.cs b/SharpTreeView/EditTextBox.cs index fd5fcd3a7..0b049fb5f 100644 --- a/SharpTreeView/EditTextBox.cs +++ b/SharpTreeView/EditTextBox.cs @@ -27,8 +27,7 @@ namespace ICSharpCode.TreeView public SharpTreeViewItem Item { get; set; } - public SharpTreeNode Node - { + public SharpTreeNode Node { get { return Item.Node; } } @@ -43,8 +42,7 @@ namespace ICSharpCode.TreeView { if (e.Key == Key.Enter) { Commit(); - } - else if (e.Key == Key.Escape) { + } else if (e.Key == Key.Escape) { Node.IsEditing = false; } } diff --git a/SharpTreeView/FlatListTreeNode.cs b/SharpTreeView/FlatListTreeNode.cs index cf30a85e3..4f5a4d544 100644 --- a/SharpTreeView/FlatListTreeNode.cs +++ b/SharpTreeView/FlatListTreeNode.cs @@ -364,6 +364,8 @@ namespace ICSharpCode.TreeView Debug.Assert(node.listParent == null); Debug.Assert(node.left == null); Debug.Assert(node.right == null); + node.height = 1; + node.totalListLength = -1; if (balancingNode != null) RebalanceUntilRoot(balancingNode); } diff --git a/SharpTreeView/Properties/GlobalAssemblyInfo.cs b/SharpTreeView/Properties/GlobalAssemblyInfo.cs index f7639fad4..3e81bb71a 100644 --- a/SharpTreeView/Properties/GlobalAssemblyInfo.cs +++ b/SharpTreeView/Properties/GlobalAssemblyInfo.cs @@ -16,9 +16,9 @@ using System.Reflection; [assembly: System.Runtime.InteropServices.ComVisible(false)] [assembly: AssemblyCompany("ic#code")] [assembly: AssemblyProduct("SharpDevelop")] -[assembly: AssemblyCopyright("2000-2011 AlphaSierraPapa for the SharpDevelop Team")] +[assembly: AssemblyCopyright("2000-2012 AlphaSierraPapa for the SharpDevelop Team")] [assembly: AssemblyVersion(RevisionClass.Major + "." + RevisionClass.Minor + "." + RevisionClass.Build + "." + RevisionClass.Revision)] -[assembly: AssemblyInformationalVersion(RevisionClass.FullVersion + "-d9a90d79")] +[assembly: AssemblyInformationalVersion(RevisionClass.FullVersion + "-ca8a8e28")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", @@ -27,10 +27,10 @@ using System.Reflection; internal static class RevisionClass { public const string Major = "4"; - public const string Minor = "1"; + public const string Minor = "2"; public const string Build = "0"; - public const string Revision = "7275"; - public const string VersionName = "alpha"; + public const string Revision = "8752"; + public const string VersionName = "Beta 2"; - public const string FullVersion = Major + "." + Minor + "." + Build + ".7275-alpha"; + public const string FullVersion = Major + "." + Minor + "." + Build + ".8752-Beta 2"; } diff --git a/SharpTreeView/SharpTreeNodeView.cs b/SharpTreeView/SharpTreeNodeView.cs index 5ed2815e9..78481cf81 100644 --- a/SharpTreeView/SharpTreeNodeView.cs +++ b/SharpTreeView/SharpTreeNodeView.cs @@ -38,6 +38,15 @@ namespace ICSharpCode.TreeView } public SharpTreeViewItem ParentItem { get; private set; } + + public static readonly DependencyProperty CellEditorProperty = + DependencyProperty.Register("CellEditor", typeof(Control), typeof(SharpTreeNodeView), + new FrameworkPropertyMetadata()); + + public Control CellEditor { + get { return (Control)GetValue(CellEditorProperty); } + set { SetValue(CellEditorProperty, value); } + } public SharpTreeView ParentTreeView { @@ -104,7 +113,10 @@ namespace ICSharpCode.TreeView { var textEditorContainer = Template.FindName("textEditorContainer", this) as Border; if (Node.IsEditing) { - textEditorContainer.Child = new EditTextBox() { Item = ParentItem }; + if (CellEditor == null) + textEditorContainer.Child = new EditTextBox() { Item = ParentItem }; + else + textEditorContainer.Child = CellEditor; } else { textEditorContainer.Child = null; diff --git a/SharpTreeView/SharpTreeView.cs b/SharpTreeView/SharpTreeView.cs index dc230faed..3addc9e03 100644 --- a/SharpTreeView/SharpTreeView.cs +++ b/SharpTreeView/SharpTreeView.cs @@ -176,6 +176,10 @@ namespace ICSharpCode.TreeView base.PrepareContainerForItemOverride(element, item); SharpTreeViewItem container = element as SharpTreeViewItem; container.ParentTreeView = this; + // Make sure that the line renderer takes into account the new bound data + if (container.NodeView != null) { + container.NodeView.LinesRenderer.InvalidateVisual(); + } } bool doNotScrollOnExpanding;