From 3c3ff30a5be01988dc346488c37c9cb7b1946228 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 10 Sep 2009 23:09:29 +0000 Subject: [PATCH] AvalonEdit: Improved caret/selection behavior on the border of read-only sections. Fixed some FxCop warnings. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4907 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../TextSegmentReadOnlySectionTests.cs | 135 ++++++++++++++++++ .../ICSharpCode.AvalonEdit.Tests.csproj | 2 + .../ICSharpCode.AvalonEdit/Editing/Caret.cs | 14 +- .../Editing/EditingCommandHandler.cs | 20 ++- .../Editing/SimpleSelection.cs | 4 +- .../Editing/TextArea.cs | 2 +- .../ICSharpCode.AvalonEdit.csproj | 1 + .../Xml/AXmlAttribute.cs | 5 +- .../Xml/AXmlAttributeCollection.cs | 2 +- .../Xml/AXmlContainer.cs | 2 +- .../Xml/AXmlDocument.cs | 3 +- .../ICSharpCode.AvalonEdit/Xml/AXmlElement.cs | 13 +- .../ICSharpCode.AvalonEdit/Xml/AXmlObject.cs | 9 +- .../ICSharpCode.AvalonEdit/Xml/AXmlParser.cs | 11 +- .../ICSharpCode.AvalonEdit/Xml/AXmlTag.cs | 9 +- .../ICSharpCode.AvalonEdit/Xml/AXmlText.cs | 5 +- .../Xml/CanonicalPrintAXmlVisitor.cs | 2 +- .../Xml/InternalException.cs | 49 +++++++ .../Xml/TagMatchingHeuristics.cs | 10 +- .../ICSharpCode.AvalonEdit/Xml/TagReader.cs | 17 +-- .../ICSharpCode.AvalonEdit/Xml/TokenReader.cs | 6 +- .../Xml/TrackedSegmentCollection.cs | 4 +- 22 files changed, 276 insertions(+), 49 deletions(-) create mode 100644 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Editing/TextSegmentReadOnlySectionTests.cs create mode 100644 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/InternalException.cs diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Editing/TextSegmentReadOnlySectionTests.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Editing/TextSegmentReadOnlySectionTests.cs new file mode 100644 index 0000000000..93e78cf7ca --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Editing/TextSegmentReadOnlySectionTests.cs @@ -0,0 +1,135 @@ +// +// +// +// +// $Revision$ +// + +using ICSharpCode.AvalonEdit.Document; +using System; +using System.Linq; +using NUnit.Framework; + +namespace ICSharpCode.AvalonEdit.Editing.Tests +{ + [TestFixture] + public class TextSegmentReadOnlySectionTests + { + TextSegmentCollection segments; + TextSegmentReadOnlySectionProvider provider; + + [SetUp] + public void SetUp() + { + segments = new TextSegmentCollection(); + provider = new TextSegmentReadOnlySectionProvider(segments); + } + + [Test] + public void InsertionPossibleWhenNothingIsReadOnly() + { + Assert.IsTrue(provider.CanInsert(0)); + Assert.IsTrue(provider.CanInsert(100)); + } + + [Test] + public void DeletionPossibleWhenNothingIsReadOnly() + { + var result = provider.GetDeletableSegments(new SimpleSegment(10, 20)).ToList(); + Assert.AreEqual(1, result.Count); + Assert.AreEqual(10, result[0].Offset); + Assert.AreEqual(20, result[0].Length); + } + + [Test] + public void InsertionPossibleBeforeReadOnlySegment() + { + segments.Add(new TextSegment { StartOffset = 10, EndOffset = 15 }); + Assert.IsTrue(provider.CanInsert(5)); + } + + [Test] + public void InsertionPossibleAtStartOfReadOnlySegment() + { + segments.Add(new TextSegment { StartOffset = 10, EndOffset = 15 }); + Assert.IsTrue(provider.CanInsert(10)); + } + + [Test] + public void InsertionImpossibleInsideReadOnlySegment() + { + segments.Add(new TextSegment { StartOffset = 10, EndOffset = 15 }); + Assert.IsFalse(provider.CanInsert(11)); + Assert.IsFalse(provider.CanInsert(12)); + Assert.IsFalse(provider.CanInsert(13)); + Assert.IsFalse(provider.CanInsert(14)); + } + + [Test] + public void InsertionPossibleAtEndOfReadOnlySegment() + { + segments.Add(new TextSegment { StartOffset = 10, EndOffset = 15 }); + Assert.IsTrue(provider.CanInsert(15)); + } + + [Test] + public void InsertionPossibleBetweenReadOnlySegments() + { + segments.Add(new TextSegment { StartOffset = 10, EndOffset = 15 }); + segments.Add(new TextSegment { StartOffset = 15, EndOffset = 20 }); + Assert.IsTrue(provider.CanInsert(15)); + } + + [Test] + public void DeletionImpossibleInReadOnlySegment() + { + segments.Add(new TextSegment { StartOffset = 10, Length = 5 }); + var result = provider.GetDeletableSegments(new SimpleSegment(11, 2)).ToList(); + Assert.AreEqual(0, result.Count); + } + + [Test] + public void DeletionAroundReadOnlySegment() + { + segments.Add(new TextSegment { StartOffset = 20, Length = 5 }); + var result = provider.GetDeletableSegments(new SimpleSegment(15, 16)).ToList(); + Assert.AreEqual(2, result.Count); + Assert.AreEqual(15, result[0].Offset); + Assert.AreEqual(5, result[0].Length); + Assert.AreEqual(25, result[1].Offset); + Assert.AreEqual(6, result[1].Length); + } + + [Test] + public void DeleteLastCharacterInReadOnlySegment() + { + segments.Add(new TextSegment { StartOffset = 20, Length = 5 }); + var result = provider.GetDeletableSegments(new SimpleSegment(24, 1)).ToList(); + Assert.AreEqual(0, result.Count); + /* // we would need this result for the old Backspace code so that the last character doesn't get selected: + Assert.AreEqual(1, result.Count); + Assert.AreEqual(25, result[0].Offset); + Assert.AreEqual(0, result[0].Length);*/ + } + + [Test] + public void DeleteFirstCharacterInReadOnlySegment() + { + segments.Add(new TextSegment { StartOffset = 20, Length = 5 }); + var result = provider.GetDeletableSegments(new SimpleSegment(20, 1)).ToList(); + Assert.AreEqual(0, result.Count); + /* // we would need this result for the old Delete code so that the first character doesn't get selected: + Assert.AreEqual(1, result.Count); + Assert.AreEqual(2, result[0].Offset); + Assert.AreEqual(0, result[0].Length);*/ + } + + [Test] + public void DeleteWholeReadOnlySegment() + { + segments.Add(new TextSegment { StartOffset = 20, Length = 5 }); + var result = provider.GetDeletableSegments(new SimpleSegment(20, 5)).ToList(); + Assert.AreEqual(0, result.Count); + } + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj index 88e181d1ab..42223b2802 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/ICSharpCode.AvalonEdit.Tests.csproj @@ -71,6 +71,7 @@ + @@ -90,6 +91,7 @@ + diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs index e5dc94416d..7116a809c6 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs @@ -85,7 +85,7 @@ namespace ICSharpCode.AvalonEdit.Editing if (PositionChanged != null) { PositionChanged(this, EventArgs.Empty); } - Debug.WriteLine("Caret position changed to " + value); + Log("Caret position changed to " + value); if (visible) Show(); } @@ -232,6 +232,7 @@ namespace ICSharpCode.AvalonEdit.Editing if (!visualColumnValid) { TextDocument document = textArea.Document; if (document != null) { + Debug.WriteLine("Explicit validation of caret column"); var documentLine = document.GetLineByNumber(position.Line); RevalidateVisualColumn(textView.GetOrConstructVisualLine(documentLine)); } @@ -329,7 +330,7 @@ namespace ICSharpCode.AvalonEdit.Editing /// public void Show() { - Debug.WriteLine("Caret.Show()"); + Log("Caret.Show()"); visible = true; if (!showScheduled) { showScheduled = true; @@ -372,7 +373,7 @@ namespace ICSharpCode.AvalonEdit.Editing /// public void Hide() { - Debug.WriteLine("Caret.Hide()"); + Log("Caret.Hide()"); visible = false; if (hasWin32Caret) { Win32.DestroyCaret(); @@ -383,6 +384,13 @@ namespace ICSharpCode.AvalonEdit.Editing } } + [Conditional("DEBUG")] + static void Log(string text) + { + // commented out to make debug output less noisy - add back if there are any problems with the caret + //Debug.WriteLine(text); + } + /// /// Gets/Sets the color of the caret. /// diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs index 49ab029bf7..f81a1e6c45 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs @@ -226,8 +226,23 @@ namespace ICSharpCode.AvalonEdit.Editing // call BeginUpdate before running the 'selectingCommand' // so that undoing the delete does not select the deleted character using (textArea.Document.RunUpdate()) { - if (textArea.Selection.IsEmpty) + if (textArea.Selection.IsEmpty) { + TextViewPosition oldCaretPosition = textArea.Caret.Position; selectingCommand.Execute(args.Parameter, textArea); + bool hasSomethingDeletable = false; + foreach (ISegment s in textArea.Selection.Segments) { + if (textArea.GetDeletableSegments(s).Length > 0) { + hasSomethingDeletable = true; + break; + } + } + if (!hasSomethingDeletable) { + // If nothing in the selection is deletable; then reset caret+selection + // to the previous value. This prevents the caret from moving through read-only sections. + textArea.Caret.Position = oldCaretPosition; + textArea.Selection = Selection.Empty; + } + } textArea.RemoveSelectedText(); } textArea.Caret.BringCaretToView(); @@ -362,7 +377,8 @@ namespace ICSharpCode.AvalonEdit.Editing TextArea textArea = GetTextArea(target); if (textArea != null && textArea.Document != null) { DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line); - textArea.Document.Remove(currentLine.Offset, currentLine.TotalLength); + textArea.Selection = new SimpleSelection(currentLine.Offset, currentLine.EndOffset); + textArea.RemoveSelectedText(); args.Handled = true; } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs index 46f284019b..57a734e79f 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs @@ -86,7 +86,9 @@ namespace ICSharpCode.AvalonEdit.Editing textArea.Document.Remove(segmentsToDelete[i]); } } - textArea.Selection = Selection.Empty; + if (segmentsToDelete.Length != 0) { + textArea.Selection = Selection.Empty; + } } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs index 041d2be520..df945327ab 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/TextArea.cs @@ -351,7 +351,7 @@ namespace ICSharpCode.AvalonEdit.Editing { ensureSelectionValidRequested = false; if (allowCaretOutsideSelection == 0) { - if (!selection.Contains(caret.Offset)) { + if (!selection.IsEmpty && !selection.Contains(caret.Offset)) { Debug.WriteLine("Resetting selection because caret is outside"); this.Selection = Selection.Empty; } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj index bd8173680d..8d2c0da60b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj @@ -321,6 +321,7 @@ + AXmlParser.cs diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttribute.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttribute.cs index f28ac40998..3846066c6a 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttribute.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttribute.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; +using System.Globalization; using System.Linq; using ICSharpCode.AvalonEdit.Document; @@ -80,7 +81,7 @@ namespace ICSharpCode.AvalonEdit.Xml AXmlElement elem = this.ParentElement; if (elem != null) { - return elem.ReslovePrefix(this.Prefix); + return elem.ResolvePrefix(this.Prefix); } return NoNamespace; // Orphaned attribute } @@ -126,7 +127,7 @@ namespace ICSharpCode.AvalonEdit.Xml /// public override string ToString() { - return string.Format("[{0} '{1}{2}{3}']", base.ToString(), this.Name, this.EqualsSign, this.Value); + return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}{2}{3}']", base.ToString(), this.Name, this.EqualsSign, this.Value); } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttributeCollection.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttributeCollection.cs index bbd68e9db1..a3120978e9 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttributeCollection.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlAttributeCollection.cs @@ -16,7 +16,7 @@ namespace ICSharpCode.AvalonEdit.Xml public class AXmlAttributeCollection: FilteredCollection> { /// Empty unbound collection - public static AXmlAttributeCollection Empty = new AXmlAttributeCollection(); + public static readonly AXmlAttributeCollection Empty = new AXmlAttributeCollection(); /// Create unbound collection protected AXmlAttributeCollection() {} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlContainer.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlContainer.cs index 87ef826cb5..43e754dbb9 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlContainer.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlContainer.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.AvalonEdit.Xml public AXmlObjectCollection Children { get; private set; } /// Create new container - public AXmlContainer() + protected AXmlContainer() { this.Children = new AXmlObjectCollection(); } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlDocument.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlDocument.cs index bd0e6c0719..59890d0e4c 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlDocument.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlDocument.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; +using System.Globalization; using System.Linq; using ICSharpCode.AvalonEdit.Document; @@ -66,7 +67,7 @@ namespace ICSharpCode.AvalonEdit.Xml /// public override string ToString() { - return string.Format("[{0} Chld:{1}]", base.ToString(), this.Children.Count); + return string.Format(CultureInfo.InvariantCulture, "[{0} Chld:{1}]", base.ToString(), this.Children.Count); } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlElement.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlElement.cs index d6cca5d474..782fc0b11f 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlElement.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlElement.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; +using System.Globalization; using System.Linq; using ICSharpCode.AvalonEdit.Document; @@ -144,15 +145,15 @@ namespace ICSharpCode.AvalonEdit.Xml get { string prefix = this.Prefix; if (string.IsNullOrEmpty(prefix)) { - return FindDefaultNamesapce(); + return FindDefaultNamespace(); } else { - return ReslovePrefix(prefix); + return ResolvePrefix(prefix); } } } - /// Find the defualt namesapce for this context - public string FindDefaultNamesapce() + /// Find the defualt namespace for this context + public string FindDefaultNamespace() { AXmlElement current = this; while(current != null) { @@ -167,7 +168,7 @@ namespace ICSharpCode.AvalonEdit.Xml /// Recursively resolve given prefix in this context. Prefix must have some value. /// /// Empty string if prefix is not found - public string ReslovePrefix(string prefix) + public string ResolvePrefix(string prefix) { if (string.IsNullOrEmpty(prefix)) throw new ArgumentException("No prefix given", "prefix"); @@ -223,7 +224,7 @@ namespace ICSharpCode.AvalonEdit.Xml /// public override string ToString() { - return string.Format("[{0} '{1}' Attr:{2} Chld:{3} Nest:{4}]", base.ToString(), this.Name, this.HasStartOrEmptyTag ? this.StartTag.Children.Count : 0, this.Children.Count, this.IsProperlyNested ? "Ok" : "Bad"); + return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}' Attr:{2} Chld:{3} Nest:{4}]", base.ToString(), this.Name, this.HasStartOrEmptyTag ? this.StartTag.Children.Count : 0, this.Children.Count, this.IsProperlyNested ? "Ok" : "Bad"); } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObject.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObject.cs index 96244b0261..9943dbbc02 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObject.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlObject.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; +using System.Globalization; using System.Linq; using ICSharpCode.AvalonEdit.Document; @@ -45,7 +46,7 @@ namespace ICSharpCode.AvalonEdit.Xml internal AXmlDocument Document { get; set; } /// Creates new object - public AXmlObject() + protected AXmlObject() { this.LastUpdatedFrom = this; } @@ -129,7 +130,7 @@ namespace ICSharpCode.AvalonEdit.Xml protected static void Assert(bool condition, string message) { if (!condition) { - throw new Exception("Assertion failed: " + message); + throw new InternalException("Assertion failed: " + message); } } @@ -138,7 +139,7 @@ namespace ICSharpCode.AvalonEdit.Xml protected static void DebugAssert(bool condition, string message) { if (!condition) { - throw new Exception("Assertion failed: " + message); + throw new InternalException("Assertion failed: " + message); } } @@ -229,7 +230,7 @@ namespace ICSharpCode.AvalonEdit.Xml /// public override string ToString() { - return string.Format("{0}({1}-{2})", this.GetType().Name.Remove(0, 4), this.StartOffset, this.EndOffset); + return string.Format(CultureInfo.InvariantCulture, "{0}({1}-{2})", this.GetType().Name.Remove(0, 4), this.StartOffset, this.EndOffset); } #region Helpper methods diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlParser.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlParser.cs index 4c96f98922..56dc5278f5 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlParser.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlParser.cs @@ -6,12 +6,13 @@ // using System; -using System.Linq; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading; using ICSharpCode.AvalonEdit.Document; -using System.Threading; namespace ICSharpCode.AvalonEdit.Xml { @@ -115,7 +116,7 @@ namespace ICSharpCode.AvalonEdit.Xml internal static void Assert(bool condition, string message) { if (!condition) { - throw new Exception("Assertion failed: " + message); + throw new InternalException("Assertion failed: " + message); } } @@ -124,14 +125,14 @@ namespace ICSharpCode.AvalonEdit.Xml internal static void DebugAssert(bool condition, string message) { if (!condition) { - throw new Exception("Assertion failed: " + message); + throw new InternalException("Assertion failed: " + message); } } [Conditional("DEBUG")] internal static void Log(string text, params object[] pars) { - System.Diagnostics.Debug.WriteLine(string.Format("XML: " + text, pars)); + System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "XML: " + text, pars)); } /// diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs index c96c323ac3..4eff85d724 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlTag.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; +using System.Globalization; using System.Linq; using ICSharpCode.AvalonEdit.Document; @@ -22,7 +23,9 @@ namespace ICSharpCode.AvalonEdit.Xml public class AXmlTag: AXmlContainer { /// These identify the start of DTD elements - public static readonly string[] DTDNames = new string[] {" DtdNames = new ReadOnlyCollection( + new string[] {" Opening bracket - usually "<" public string OpeningBracket { get; internal set; } @@ -46,7 +49,7 @@ namespace ICSharpCode.AvalonEdit.Xml /// True if tag starts with "<![CDATA[" public bool IsCData { get { return OpeningBracket == " True if tag starts with one of the DTD starts - public bool IsDocumentType { get { return DTDNames.Contains(OpeningBracket); } } + public bool IsDocumentType { get { return DtdNames.Contains(OpeningBracket); } } /// True if tag starts with "<!" public bool IsUnknownBang { get { return OpeningBracket == " public override string ToString() { - return string.Format("[{0} '{1}{2}{3}' Attr:{4}]", base.ToString(), this.OpeningBracket, this.Name, this.ClosingBracket, this.Children.Count); + return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}{2}{3}' Attr:{4}]", base.ToString(), this.OpeningBracket, this.Name, this.ClosingBracket, this.Children.Count); } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlText.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlText.cs index 970e130a22..087faaaaa6 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlText.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/AXmlText.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; +using System.Globalization; using System.Linq; using ICSharpCode.AvalonEdit.Document; @@ -28,6 +29,8 @@ namespace ICSharpCode.AvalonEdit.Xml /// The text with all entity references resloved public string Value { get; set; } /// True if the text contains only whitespace characters + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace", + Justification = "System.Xml also uses 'Whitespace'")] public bool ContainsOnlyWhitespace { get; set; } /// @@ -57,7 +60,7 @@ namespace ICSharpCode.AvalonEdit.Xml /// public override string ToString() { - return string.Format("[{0} Text.Length={1}]", base.ToString(), this.EscapedValue.Length); + return string.Format(CultureInfo.InvariantCulture, "[{0} Text.Length={1}]", base.ToString(), this.EscapedValue.Length); } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/CanonicalPrintAXmlVisitor.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/CanonicalPrintAXmlVisitor.cs index ab5e2ffd4b..eaaae645a5 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/CanonicalPrintAXmlVisitor.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/CanonicalPrintAXmlVisitor.cs @@ -104,7 +104,7 @@ namespace ICSharpCode.AvalonEdit.Xml sb.Append(Escape(text.Value)); } - string Escape(string text) + static string Escape(string text) { return text .Replace("&", "&") diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/InternalException.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/InternalException.cs new file mode 100644 index 0000000000..c5dc837b19 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/InternalException.cs @@ -0,0 +1,49 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Runtime.Serialization; + +namespace ICSharpCode.AvalonEdit.Xml +{ + /// + /// Exception used for internal errors in XML parser. + /// This exception indicates a bug in AvalonEdit. + /// + [Serializable()] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "This exception is not public because it is not supposed to be caught by user code - it indicates a bug in AvalonEdit.")] + class InternalException : Exception + { + /// + /// Creates a new InternalException instance. + /// + public InternalException() : base() + { + } + + /// + /// Creates a new InternalException instance. + /// + public InternalException(string message) : base(message) + { + } + + /// + /// Creates a new InternalException instance. + /// + public InternalException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Creates a new InternalException instance. + /// + protected InternalException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagMatchingHeuristics.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagMatchingHeuristics.cs index 9f814111db..f4b9ad076d 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagMatchingHeuristics.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagMatchingHeuristics.cs @@ -56,7 +56,7 @@ namespace ICSharpCode.AvalonEdit.Xml } // Check well formed - foreach(AXmlTag xmlDeclaration in doc.Children.OfType().Where(t => t.IsProcessingInstruction && t.Name.ToLower() == "xml")) { + foreach(AXmlTag xmlDeclaration in doc.Children.OfType().Where(t => t.IsProcessingInstruction && string.Equals(t.Name, "xml", StringComparison.OrdinalIgnoreCase))) { if (xmlDeclaration.StartOffset != 0) TagReader.OnSyntaxError(doc, xmlDeclaration.StartOffset, xmlDeclaration.StartOffset + 5, "XML declaration must be at the start of document"); @@ -87,7 +87,7 @@ namespace ICSharpCode.AvalonEdit.Xml return doc; } - AXmlObject ReadSingleObject(IEnumerator objStream) + static AXmlObject ReadSingleObject(IEnumerator objStream) { AXmlObject obj = objStream.Current; objStream.MoveNext(); @@ -407,7 +407,7 @@ namespace ICSharpCode.AvalonEdit.Xml } #region Helper methods - + /* string PrintObjects(IEnumerable objs) { StringBuilder sb = new StringBuilder(); @@ -429,12 +429,12 @@ namespace ICSharpCode.AvalonEdit.Xml } else if (obj is AXmlText) { sb.Append('~'); } else { - throw new Exception("Should not be here: " + obj); + throw new InternalException("Should not be here: " + obj); } } return sb.ToString(); } - + */ #endregion } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs index ca66ffeb1c..252f299574 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TagReader.cs @@ -149,7 +149,7 @@ namespace ICSharpCode.AvalonEdit.Xml } else if (tag.IsUnknownBang) { text = ReadText(TextType.UnknownBang); } else { - throw new Exception(string.Format("Unknown opening bracket '{0}'", tag.OpeningBracket)); + throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket)); } // Enumerate text = text.ToList(); @@ -186,7 +186,7 @@ namespace ICSharpCode.AvalonEdit.Xml } else if (tag.IsDocumentType) { if (tag.ClosingBracket != ">") OnSyntaxError(tag, brStart, brEnd, "'>' expected"); } else { - throw new Exception(string.Format("Unknown opening bracket '{0}'", tag.OpeningBracket)); + throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket)); } // Attribute name may not apper multiple times @@ -220,7 +220,7 @@ namespace ICSharpCode.AvalonEdit.Xml } else if (TryRead("[CDATA[")) { return " inputLength) throw new Exception("Skipping after the end of file"); + AXmlParser.Assert(currentLocation + count <= inputLength, "Skipping after the end of file"); currentLocation += count; } protected void GoBack(int oldLocation) { - if (oldLocation > currentLocation) throw new Exception("Trying to move forward"); + AXmlParser.Assert(oldLocation <= currentLocation, "Trying to move forward"); maxTouchedLocation = Math.Max(maxTouchedLocation, currentLocation); currentLocation = oldLocation; } @@ -269,7 +269,7 @@ namespace ICSharpCode.AvalonEdit.Xml protected string GetText(int start, int end) { - if (end > currentLocation) throw new Exception("Reading ahead of current location"); + AXmlParser.Assert(end <= currentLocation, "Reading ahead of current location"); if (start == inputLength && end == inputLength) { return string.Empty; } else { diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TrackedSegmentCollection.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TrackedSegmentCollection.cs index 7aebb3160d..b8c59f7c50 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TrackedSegmentCollection.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Xml/TrackedSegmentCollection.cs @@ -7,7 +7,9 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; + using ICSharpCode.AvalonEdit.Document; namespace ICSharpCode.AvalonEdit.Xml @@ -70,7 +72,7 @@ namespace ICSharpCode.AvalonEdit.Xml public void AddParsedObject(AXmlObject obj, int? maxTouchedLocation) { if (!(obj.Length > 0 || obj is AXmlDocument)) - AXmlParser.Assert(false, string.Format("Invalid object {0}. It has zero length.", obj)); + AXmlParser.Assert(false, string.Format(CultureInfo.InvariantCulture, "Invalid object {0}. It has zero length.", obj)); // // Expensive check // if (obj is AXmlContainer) { // int objStartOffset = obj.StartOffset;