Browse Source

AvalonEdit: add safety check that ReadOnlySectionProvider doesn't return invalid sections.

Core.Presentation: fixed typo in class name

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4891 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
9761f2de43
  1. 49
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/ISegment.cs
  2. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegment.cs
  3. 4
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
  4. 4
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/IReadOnlySectionProvider.cs
  5. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/RectangleSelection.cs
  6. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs
  7. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs
  8. 19
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs
  9. 2
      src/Main/Base/Project/Src/Gui/AbstractViewContent.cs
  10. 6
      src/Main/ICSharpCode.Core.Presentation/LocalizeExtension.cs
  11. 2
      src/Main/ICSharpCode.Core.Presentation/StringParseExtension.cs

49
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/ISegment.cs

@ -5,6 +5,7 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.AvalonEdit.Utils;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@ -130,44 +131,74 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// A segment using text anchors as start and end positions. /// A segment using text anchors as start and end positions.
/// </summary> /// </summary>
sealed class AnchorSegment : ISegment /// <remarks>
/// For the constructors creating new anchors, the start position will be AfterInsertion and the end position will be BeforeInsertion.
/// Should the end position move before the start position, the segment will have length 0.
/// </remarks>
public sealed class AnchorSegment : ISegment
{ {
readonly TextAnchor start, end; readonly TextAnchor start, end;
/// <inheritdoc/>
public int Offset { public int Offset {
get { return start.Offset; } get { return start.Offset; }
} }
/// <inheritdoc/>
public int Length { public int Length {
get { return end.Offset - start.Offset; } get {
// Math.Max takes care of the fact that end.Offset might move before start.Offset.
return Math.Max(0, end.Offset - start.Offset);
}
} }
/// <inheritdoc/>
public int EndOffset { public int EndOffset {
get { return end.Offset; } get {
// Math.Max takes care of the fact that end.Offset might move before start.Offset.
return Math.Max(start.Offset, end.Offset);
}
} }
/// <summary>
/// Creates a new AnchorSegment using the specified anchors.
/// The anchors must have <see cref="TextAnchor.SurviveDeletion"/> set to true.
/// </summary>
public AnchorSegment(TextAnchor start, TextAnchor end) public AnchorSegment(TextAnchor start, TextAnchor end)
{ {
Debug.Assert(start != null); if (start == null)
Debug.Assert(end != null); throw new ArgumentNullException("start");
Debug.Assert(start.SurviveDeletion); if (end == null)
Debug.Assert(end.SurviveDeletion); throw new ArgumentNullException("end");
if (!start.SurviveDeletion)
throw new ArgumentException("Anchors for AnchorSegment must use SurviveDeletion", "start");
if (!end.SurviveDeletion)
throw new ArgumentException("Anchors for AnchorSegment must use SurviveDeletion", "end");
this.start = start; this.start = start;
this.end = end; this.end = end;
} }
/// <summary>
/// Creates a new AnchorSegment that creates new anchors.
/// </summary>
public AnchorSegment(TextDocument document, ISegment segment) public AnchorSegment(TextDocument document, ISegment segment)
: this(document, segment.Offset, segment.Length) : this(document, ThrowUtil.CheckNotNull(segment, "segment").Offset, segment.Length)
{ {
} }
/// <summary>
/// Creates a new AnchorSegment that creates new anchors.
/// </summary>
public AnchorSegment(TextDocument document, int offset, int length) public AnchorSegment(TextDocument document, int offset, int length)
{ {
Debug.Assert(document != null); if (document == null)
throw new ArgumentNullException("document");
this.start = document.CreateAnchor(offset); this.start = document.CreateAnchor(offset);
this.start.SurviveDeletion = true; this.start.SurviveDeletion = true;
this.start.MovementType = AnchorMovementType.AfterInsertion;
this.end = document.CreateAnchor(offset + length); this.end = document.CreateAnchor(offset + length);
this.end.SurviveDeletion = true; this.end.SurviveDeletion = true;
this.start.MovementType = AnchorMovementType.BeforeInsertion;
} }
} }
} }

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextSegment.cs

@ -109,8 +109,11 @@ namespace ICSharpCode.AvalonEdit.Document
} }
/// <summary> /// <summary>
/// Gets the end offset of the segment. /// Gets/Sets the end offset of the segment.
/// </summary> /// </summary>
/// <remarks>
/// Setting the end offset will change the length, the start offset will stay constant.
/// </remarks>
public int EndOffset { public int EndOffset {
get { get {
return StartOffset + Length; return StartOffset + Length;
@ -126,6 +129,9 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Gets/Sets the length of the segment. /// Gets/Sets the length of the segment.
/// </summary> /// </summary>
/// <remarks>
/// Setting the length will change the end offset, the start offset will stay constant.
/// </remarks>
public int Length { public int Length {
get { get {
return segmentLength; return segmentLength;

4
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs

@ -151,7 +151,7 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
if (segments != null) { if (segments != null) {
foreach (ISegment segment in segments.Reverse()) { foreach (ISegment segment in segments.Reverse()) {
foreach (ISegment writableSegment in textArea.ReadOnlySectionProvider.GetDeletableSegments(segment).Reverse()) { foreach (ISegment writableSegment in textArea.GetDeletableSegments(segment).Reverse()) {
transformSegment(textArea, writableSegment); transformSegment(textArea, writableSegment);
} }
} }
@ -208,7 +208,7 @@ namespace ICSharpCode.AvalonEdit.Editing
int offset = line.Offset; int offset = line.Offset;
ISegment s = TextUtilities.GetSingleIndentationSegment(line.Document, offset, textArea.Options.IndentationSize); ISegment s = TextUtilities.GetSingleIndentationSegment(line.Document, offset, textArea.Options.IndentationSize);
if (s.Length > 0) { if (s.Length > 0) {
s = textArea.ReadOnlySectionProvider.GetDeletableSegments(s).FirstOrDefault(); s = textArea.GetDeletableSegments(s).FirstOrDefault();
if (s != null && s.Length > 0) { if (s != null && s.Length > 0) {
textArea.Document.Remove(s.Offset, s.Length); textArea.Document.Remove(s.Offset, s.Length);
} }

4
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/IReadOnlySectionProvider.cs

@ -24,6 +24,10 @@ namespace ICSharpCode.AvalonEdit.Editing
/// <summary> /// <summary>
/// Gets the deletable segments inside the given segment. /// Gets the deletable segments inside the given segment.
/// </summary> /// </summary>
/// <remarks>
/// All segments in the result must be within the given segment, and they must be returned in order
/// (e.g. if two segments are returned, EndOffset of first segment must be less than StartOffset of second segment).
/// </remarks>
IEnumerable<ISegment> GetDeletableSegments(ISegment segment); IEnumerable<ISegment> GetDeletableSegments(ISegment segment);
} }
} }

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/RectangleSelection.cs

@ -215,9 +215,9 @@ namespace ICSharpCode.AvalonEdit.Editing
textArea.Document.Insert(lineSegment.Offset, newText); textArea.Document.Insert(lineSegment.Offset, newText);
} }
} else { } else {
var segmentsToDelete = textArea.ReadOnlySectionProvider.GetDeletableSegments(lineSegment).ToList(); ISegment[] segmentsToDelete = textArea.GetDeletableSegments(lineSegment);
for (int i = segmentsToDelete.Count - 1; i >= 0; i--) { for (int i = segmentsToDelete.Length - 1; i >= 0; i--) {
if (i == segmentsToDelete.Count - 1) { if (i == segmentsToDelete.Length - 1) {
textArea.Document.Replace(segmentsToDelete[i], newText); textArea.Document.Replace(segmentsToDelete[i], newText);
} else { } else {
textArea.Document.Remove(segmentsToDelete[i]); textArea.Document.Remove(segmentsToDelete[i]);

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionMouseHandler.cs

@ -270,7 +270,7 @@ namespace ICSharpCode.AvalonEdit.Editing
DragDropEffects allowedEffects = DragDropEffects.All; DragDropEffects allowedEffects = DragDropEffects.All;
var deleteOnMove = textArea.Selection.Segments.Select(s => new AnchorSegment(textArea.Document, s)).ToList(); var deleteOnMove = textArea.Selection.Segments.Select(s => new AnchorSegment(textArea.Document, s)).ToList();
foreach (ISegment s in deleteOnMove) { foreach (ISegment s in deleteOnMove) {
ISegment[] result = textArea.ReadOnlySectionProvider.GetDeletableSegments(s).ToArray(); ISegment[] result = textArea.GetDeletableSegments(s);
if (result.Length != 1 || result[0].Offset != s.Offset || result[0].EndOffset != s.EndOffset) { if (result.Length != 1 || result[0].Offset != s.Offset || result[0].EndOffset != s.EndOffset) {
allowedEffects &= ~DragDropEffects.Move; allowedEffects &= ~DragDropEffects.Move;
} }

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs

@ -77,9 +77,9 @@ namespace ICSharpCode.AvalonEdit.Editing
} }
} }
} else { } else {
var segmentsToDelete = textArea.ReadOnlySectionProvider.GetDeletableSegments(this).ToList(); ISegment[] segmentsToDelete = textArea.GetDeletableSegments(this);
for (int i = segmentsToDelete.Count - 1; i >= 0; i--) { for (int i = segmentsToDelete.Length - 1; i >= 0; i--) {
if (i == segmentsToDelete.Count - 1) { if (i == segmentsToDelete.Length - 1) {
textArea.Caret.Offset = segmentsToDelete[i].EndOffset; textArea.Caret.Offset = segmentsToDelete[i].EndOffset;
textArea.Document.Replace(segmentsToDelete[i], newText); textArea.Document.Replace(segmentsToDelete[i], newText);
} else { } else {

19
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs

@ -700,7 +700,7 @@ namespace ICSharpCode.AvalonEdit.Editing
#if DEBUG #if DEBUG
if (!selection.IsEmpty) { if (!selection.IsEmpty) {
foreach (ISegment s in selection.Segments) { foreach (ISegment s in selection.Segments) {
Debug.Assert(ReadOnlySectionProvider.GetDeletableSegments(s).Count() == 0); Debug.Assert(this.ReadOnlySectionProvider.GetDeletableSegments(s).Count() == 0);
} }
} }
#endif #endif
@ -714,6 +714,23 @@ namespace ICSharpCode.AvalonEdit.Editing
throw ThrowUtil.NoDocumentAssigned(); throw ThrowUtil.NoDocumentAssigned();
selection.ReplaceSelectionWithText(this, newText); selection.ReplaceSelectionWithText(this, newText);
} }
internal ISegment[] GetDeletableSegments(ISegment segment)
{
var deletableSegments = this.ReadOnlySectionProvider.GetDeletableSegments(segment);
if (deletableSegments == null)
throw new InvalidOperationException("ReadOnlySectionProvider.GetDeletableSegments returned null");
var array = deletableSegments.ToArray();
int lastIndex = segment.Offset;
for (int i = 0; i < array.Length; i++) {
if (array[i].Offset < lastIndex)
throw new InvalidOperationException("ReadOnlySectionProvider returned incorrect segments (outside of input segment / wrong order)");
lastIndex = array[i].EndOffset;
}
if (lastIndex > segment.EndOffset)
throw new InvalidOperationException("ReadOnlySectionProvider returned incorrect segments (outside of input segment / wrong order)");
return array;
}
#endregion #endregion
#region IndentationStrategy property #region IndentationStrategy property

2
src/Main/Base/Project/Src/Gui/AbstractViewContent.cs

@ -341,7 +341,7 @@ namespace ICSharpCode.SharpDevelop.Gui
} }
string titleName; string titleName;
LanguageDependendExtension titleNameLocalizeExtension; LanguageDependentExtension titleNameLocalizeExtension;
string IViewContent.TitleName { string IViewContent.TitleName {
get { get {

6
src/Main/ICSharpCode.Core.Presentation/LocalizeExtension.cs

@ -17,7 +17,7 @@ namespace ICSharpCode.Core.Presentation
/// Markup extension that retrieves localized resource strings. /// Markup extension that retrieves localized resource strings.
/// </summary> /// </summary>
[MarkupExtensionReturnType(typeof(string))] [MarkupExtensionReturnType(typeof(string))]
public sealed class LocalizeExtension : LanguageDependendExtension public sealed class LocalizeExtension : LanguageDependentExtension
{ {
public LocalizeExtension(string key) public LocalizeExtension(string key)
{ {
@ -48,9 +48,9 @@ namespace ICSharpCode.Core.Presentation
} }
} }
public abstract class LanguageDependendExtension : MarkupExtension, INotifyPropertyChanged, IWeakEventListener public abstract class LanguageDependentExtension : MarkupExtension, INotifyPropertyChanged, IWeakEventListener
{ {
protected LanguageDependendExtension() protected LanguageDependentExtension()
{ {
this.UpdateOnLanguageChange = true; this.UpdateOnLanguageChange = true;
} }

2
src/Main/ICSharpCode.Core.Presentation/StringParseExtension.cs

@ -18,7 +18,7 @@ namespace ICSharpCode.Core.Presentation
/// Markup extension that works like StringParser.Parse /// Markup extension that works like StringParser.Parse
/// </summary> /// </summary>
[MarkupExtensionReturnType(typeof(string))] [MarkupExtensionReturnType(typeof(string))]
public sealed class StringParseExtension : LanguageDependendExtension public sealed class StringParseExtension : LanguageDependentExtension
{ {
string text; string text;

Loading…
Cancel
Save