Browse Source

AvalonEdit: Fixed issue that caused the collapsed line state in the HeightTree to become inconsistent with the state in the folding manager.

This bug was causing strange scrolling behavior when a file with collapsed folding sections got reloaded due to external changes.
pull/6/merge
Daniel Grunwald 14 years ago
parent
commit
02f6c641cf
  1. 26
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs
  2. 35
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingSection.cs
  3. 41
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/CollapsedLineSection.cs
  4. 16
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

26
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs

@ -68,13 +68,16 @@ namespace ICSharpCode.AvalonEdit.Folding @@ -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 @@ -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 @@ -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)

35
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingSection.cs

@ -2,6 +2,7 @@ @@ -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 @@ -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 @@ -45,6 +51,9 @@ namespace ICSharpCode.AvalonEdit.Folding
}
}
/// <summary>
/// Creates new collapsed section when a text view is added to the folding manager.
/// </summary>
internal CollapsedLineSection CollapseSection(TextView textView)
{
DocumentLine startLine = manager.document.GetLineByOffset(StartOffset);
@ -56,6 +65,30 @@ namespace ICSharpCode.AvalonEdit.Folding @@ -56,6 +65,30 @@ namespace ICSharpCode.AvalonEdit.Folding
return null;
}
internal void ValidateCollapsedLineSections()
{
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);
}
}
}
}
/// <summary>
/// Gets/Sets the text used to display the collapsed version of the folding section.
/// </summary>

41
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/CollapsedLineSection.cs

@ -11,7 +11,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -11,7 +11,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// Represents a collapsed line section.
/// Use the Uncollapse() method to uncollapse the section.
/// </summary>
public sealed class CollapsedLineSection : INotifyPropertyChanged
public sealed class CollapsedLineSection
{
bool isCollapsed = true;
DocumentLine start, end;
@ -41,7 +41,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -41,7 +41,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// This property initially is true and turns to false when uncollapsing the section.
/// </summary>
public bool IsCollapsed {
get { return isCollapsed; }
get { return start != null; }
}
/// <summary>
@ -51,10 +51,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -51,10 +51,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// </summary>
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; }
}
/// <summary>
@ -64,46 +61,26 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -64,46 +61,26 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// </summary>
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; }
}
/// <summary>
/// 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.
/// </summary>
/// <exception cref="InvalidOperationException">
/// The section is already uncollapsed, or the text containing the section was deleted.
/// </exception>
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");
}
/// <summary>
/// Is raised when of the properties Start,End,IsCollapsed changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
start = null;
end = null;
}
/// <summary>
@ -113,7 +90,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -113,7 +90,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") + "]";
}
}
}

16
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

@ -999,12 +999,18 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -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);

Loading…
Cancel
Save