diff options
author | Mikayla Hutchinson <m.j.hutchinson@gmail.com> | 2018-06-29 01:47:05 +0300 |
---|---|---|
committer | Mikayla Hutchinson <m.j.hutchinson@gmail.com> | 2018-07-06 01:23:58 +0300 |
commit | f842739cb657bc02ddfd6d4f6e1de84f9fcd6df0 (patch) | |
tree | 4d1029783b13565d3274c46f4bd91411c1802728 /main | |
parent | 149eb1c5d0d1194247b9fc2dc15db415e037d757 (diff) |
[Xml] Implement Expand/Shrink selection for XML
Diffstat (limited to 'main')
-rw-r--r-- | main/src/addins/Xml/Dom/XAttributeCollection.cs | 29 | ||||
-rw-r--r-- | main/src/addins/Xml/Editor/BaseXmlEditorExtension.cs | 27 | ||||
-rw-r--r-- | main/src/addins/Xml/Editor/XmlExpandSelectionHandler.cs | 384 | ||||
-rw-r--r-- | main/src/addins/Xml/MonoDevelop.Xml.csproj | 1 | ||||
-rw-r--r-- | main/src/addins/Xml/Tests/ExpandSelectionTests.cs | 160 | ||||
-rw-r--r-- | main/src/addins/Xml/Tests/MonoDevelop.Xml.Tests.csproj | 9 |
6 files changed, 597 insertions, 13 deletions
diff --git a/main/src/addins/Xml/Dom/XAttributeCollection.cs b/main/src/addins/Xml/Dom/XAttributeCollection.cs index 0a906f0ec9..d408f5b63f 100644 --- a/main/src/addins/Xml/Dom/XAttributeCollection.cs +++ b/main/src/addins/Xml/Dom/XAttributeCollection.cs @@ -31,9 +31,11 @@ namespace MonoDevelop.Xml.Dom { public class XAttributeCollection : IEnumerable<XAttribute> { - readonly XObject parent; - XAttribute firstChild; - XAttribute lastChild; + readonly XObject parent;
+
+ public XAttribute Last { get; private set; }
+ public XAttribute First { get; private set; }
+ public int Count { get; private set; } public XAttributeCollection (XObject parent) { @@ -43,7 +45,7 @@ namespace MonoDevelop.Xml.Dom public Dictionary<XName, XAttribute> ToDictionary () { var dict = new Dictionary<XName,XAttribute> (); - XAttribute current = firstChild; + XAttribute current = First; while (current != null) { dict.Add (current.Name, current); current = current.NextSibling; @@ -53,7 +55,7 @@ namespace MonoDevelop.Xml.Dom public XAttribute this [XName name] { get { - XAttribute current = firstChild; + XAttribute current = First; while (current != null) { if (current.Name == name) return current; @@ -65,7 +67,7 @@ namespace MonoDevelop.Xml.Dom public XAttribute this [int index] { get { - XAttribute current = firstChild; + XAttribute current = First; while (current != null) { if (index == 0) return current; @@ -78,7 +80,7 @@ namespace MonoDevelop.Xml.Dom public XAttribute Get (XName name, bool ignoreCase) { - XAttribute current = firstChild; + XAttribute current = First; while (current != null) { if (XName.Equals (current.Name, name, ignoreCase)) return current; @@ -96,17 +98,18 @@ namespace MonoDevelop.Xml.Dom public void AddAttribute (XAttribute newChild) { newChild.Parent = parent; - if (lastChild != null) { - lastChild.NextSibling = newChild; + if (Last != null) { + Last.NextSibling = newChild; } - if (firstChild == null) - firstChild = newChild; - lastChild = newChild; + if (First == null) + First = newChild; + Last = newChild;
+ Count++; } public IEnumerator<XAttribute> GetEnumerator () { - XAttribute current = firstChild; + XAttribute current = First; while (current != null) { yield return current; current = current.NextSibling; diff --git a/main/src/addins/Xml/Editor/BaseXmlEditorExtension.cs b/main/src/addins/Xml/Editor/BaseXmlEditorExtension.cs index 0c2f3aedc5..6889969c84 100644 --- a/main/src/addins/Xml/Editor/BaseXmlEditorExtension.cs +++ b/main/src/addins/Xml/Editor/BaseXmlEditorExtension.cs @@ -1268,5 +1268,32 @@ namespace MonoDevelop.Xml.Editor } } } + + [CommandHandler (TextEditorCommands.ExpandSelection)] + public virtual void ExpandSelection () + { + Tracker.UpdateEngine (); + XmlExpandSelectionHandler.ExpandSelection (Editor, Tracker.Engine.GetTreeParser); + } + + [CommandUpdateHandler (TextEditorCommands.ExpandSelection)] + public void UpdateExpandSelection (CommandInfo info) + { + info.Enabled = XmlExpandSelectionHandler.CanExpandSelection (Editor); + + } + + [CommandHandler (TextEditorCommands.ShrinkSelection)] + public virtual void ShrinkSelection () + { + Tracker.UpdateEngine (); + XmlExpandSelectionHandler.ShrinkSelection (Editor, Tracker.Engine.GetTreeParser); + } + + [CommandUpdateHandler (TextEditorCommands.ShrinkSelection)] + public void UpdateShrinkSelection (CommandInfo info) + { + info.Enabled = XmlExpandSelectionHandler.CanShrinkSelection (Editor); + } } } diff --git a/main/src/addins/Xml/Editor/XmlExpandSelectionHandler.cs b/main/src/addins/Xml/Editor/XmlExpandSelectionHandler.cs new file mode 100644 index 0000000000..e4b2e79341 --- /dev/null +++ b/main/src/addins/Xml/Editor/XmlExpandSelectionHandler.cs @@ -0,0 +1,384 @@ +// +// Copyright (c) Microsoft Corp +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Xml.Dom; +using MonoDevelop.Xml.Parser; + +namespace MonoDevelop.Xml.Editor +{ + class XmlExpandSelectionHandler + { + public static bool CanExpandSelection (TextEditor editor) + { + if (!editor.IsSomethingSelected) { + return true; + } + if (editor.Selections.Count () == 1) { + return editor.SelectionRange.Offset > 0 || editor.SelectionRange.Length != editor.Length; + } + return false; + } + + internal static void ExpandSelection (TextEditor editor, Func<XmlParser> getTreeParser) + { + var selectionAnnotation = GetAnnotation (editor, getTreeParser); + if (selectionAnnotation.NodePath.Count == 0) + return; + + var newRegion = selectionAnnotation.Grow (); + if (newRegion.HasValue) { + editor.SetSelection (newRegion.Value.Begin, newRegion.Value.End); + } + } + + public static bool CanShrinkSelection (TextEditor editor) + { + return editor.IsSomethingSelected && editor.Selections.Count () == 1; + } + + internal static void ShrinkSelection (TextEditor editor, Func<XmlParser> getTreeParser) + { + var selectionAnnotation = GetAnnotation (editor, getTreeParser); + if (selectionAnnotation.NodePath.Count == 0) + return; + + var newRegion = selectionAnnotation.Shrink (); + if (newRegion.HasValue) { + editor.SetSelection (newRegion.Value.Begin, newRegion.Value.End); + } else { + editor.ClearSelection (); + } + } + + static XmlExpandSelectionAnnotation GetAnnotation (TextEditor editor, Func<XmlParser> getTreeParser) + { + var result = editor.Annotation<XmlExpandSelectionAnnotation> (); + if (result == null) { + result = new XmlExpandSelectionAnnotation (editor, getTreeParser ()); + editor.AddAnnotation (result); + } + return result; + } + + enum SelectionLevel + { + Self, + Name, + Content, + WithClosing, + Document, + Attributes + } + + class XmlExpandSelectionAnnotation + { + Stack<(int, SelectionLevel)> expansions = new Stack<(int, SelectionLevel)> (); + + readonly IReadonlyTextDocument document; + readonly TextEditor editor; + readonly XmlParser parser; + public List<XObject> NodePath { get; } + public int Index { get; set; } = -1; + public SelectionLevel Level { get; set; } + + public XmlExpandSelectionAnnotation (TextEditor editor, XmlParser parser) + { + this.parser = parser; + this.editor = editor; + document = editor.CreateDocumentSnapshot (); + editor.CaretPositionChanged += Editor_CaretPositionChanged; + NodePath = GetNodePath (parser, document); + } + + void Editor_CaretPositionChanged (object sender, EventArgs e) + { + editor.CaretPositionChanged -= Editor_CaretPositionChanged; + editor.RemoveAnnotations<XmlExpandSelectionAnnotation> (); + } + + DocumentRegion? GetCurrent () + { + if (Index < 0) { + return null; + } + var current = NodePath [Index]; + switch (Level) { + case SelectionLevel.Self: + return current.Region; + case SelectionLevel.WithClosing: + var element = (XElement)current; + return new DocumentRegion (element.Region.Begin, element.ClosingTag.Region.End); + case SelectionLevel.Name: + return current.TryGetNameRegion ().Value; + case SelectionLevel.Content: + if (current is XElement el) { + return new DocumentRegion (el.Region.End, el.ClosingTag.Region.Begin); + } + return ((XAttribute)current).GetAttributeValueRegion (document); + case SelectionLevel.Document: + return new DocumentRegion (new DocumentLocation (1, 1), document.OffsetToLocation (document.Length)); + case SelectionLevel.Attributes: + return ((XElement)current).GetAttributesRegion (); + } + throw new InvalidOperationException (); + } + + public DocumentRegion? Grow () + { + var old = (Index, Level); + if (GrowStateInternal ()) { + expansions.Push (old); + return GetCurrent (); + } + return null; + } + + bool GrowStateInternal () + { + if (Index + 1 == NodePath.Count) { + return false; + } + + //if an index is selected, we may need to transition level rather than transitioning index + if (Index >= 0) { + var current = NodePath [Index]; + if (current is XElement element) { + switch (Level) { + case SelectionLevel.Self: + if (!element.IsSelfClosing) { + Level = SelectionLevel.WithClosing; + return true; + } + break; + case SelectionLevel.Content: + Level = SelectionLevel.WithClosing; + return true; + case SelectionLevel.Name: + Level = SelectionLevel.Self; + return true; + case SelectionLevel.Attributes: + Level = SelectionLevel.Self; + return true; + } + } else if (current is XAttribute att) { + switch (Level) { + case SelectionLevel.Name: + case SelectionLevel.Content: + Level = SelectionLevel.Self; + return true; + } + } else if (Level == SelectionLevel.Name) { + Level = SelectionLevel.Self; + return true; + } else if (Level == SelectionLevel.Document) { + return false; + } + } + + //advance up the node path + Index++; + var newNode = NodePath [Index]; + + //determine the starting selection level for the new node + if (newNode is XDocument) { + Level = SelectionLevel.Document; + return true; + } + + AdvanceUntilClosed (newNode, parser, document); + + if (newNode.Region.ContainsOuter (editor.CaretLocation)) { + var nr = newNode.TryGetNameRegion (); + if (nr != null && nr.Value.ContainsOuter (editor.CaretLocation)) { + Level = SelectionLevel.Name; + return true; + } + if (newNode is XAttribute attribute) { + var valRegion = attribute.GetAttributeValueRegion (document); + if (valRegion.ContainsOuter (editor.CaretLocation)) { + Level = SelectionLevel.Content; + return true; + } + } + if (newNode is XElement xElement && xElement.Attributes.Count > 1) { + var attsRegion = xElement.GetAttributesRegion (); + if (attsRegion.ContainsOuter (editor.CaretLocation)) { + Level = SelectionLevel.Attributes; + return true; + } + } + Level = SelectionLevel.Self; + return true; + } + + if (newNode is XElement el) { + if (el.IsSelfClosing) { + Level = SelectionLevel.Self; + return true; + } + if (el.ClosingTag.Region.ContainsOuter (editor.CaretLocation)) { + Level = SelectionLevel.WithClosing; + return true; + } + Level = SelectionLevel.Content; + return true; + } + + Level = SelectionLevel.Self; + return true; + } + + public DocumentRegion? Shrink () + { + // if we have expansion state, pop it + if (expansions.Count > 0) { + var last = expansions.Pop (); + Index = last.Item1; + Level = last.Item2; + return GetCurrent (); + } + + return null; + } + + //advance the parser in chunks until the given node is complete + static void AdvanceUntilClosed (XObject ob, XmlParser parser, IReadonlyTextDocument document) + { + const int chunk = 200; + var el = ob as XElement; + while (parser.Position < document.Length) { + parser.Parse (document.CreateReader (parser.Position, Math.Min (document.Length - parser.Position, chunk))); + if (el?.IsClosed ?? ob.IsEnded || !parser.Nodes.Contains (ob.Parent)) { + break; + } + } + } + + static List<XObject> GetNodePath (XmlParser parser, IReadonlyTextDocument document) + { + int offset = parser.Position; + var length = document.Length; + int i = offset; + + var nodePath = parser.Nodes.ToList (); + + //if inside body of unclosed element, capture whole body + if (parser.CurrentState is XmlRootState && parser.Nodes.Peek () is XElement unclosedEl) { + while (i < length && InRootOrClosingTagState () && !unclosedEl.IsClosed) { + parser.Push (document.GetCharAt (i++)); + } + } + + //if in potential start of a state, capture it + else if (parser.CurrentState is XmlRootState && GetStateTag() > 0) { + //eat until we figure out whether it's a state transition + while (i < length && GetStateTag () > 0) { + parser.Push (document.GetCharAt (i++)); + } + //if it transitioned to another state, eat until we get a new node on the stack + if (NotInRootState ()) { + var newState = parser.CurrentState; + while (i < length && NotInRootState() && parser.Nodes.Count <= nodePath.Count) { + parser.Push (document.GetCharAt (i++)); + } + if (parser.Nodes.Count > nodePath.Count) { + nodePath.Insert (0, parser.Nodes.Peek ()); + } + } + } + + //ensure any unfinished names are captured + while (i < length && InNameOrAttributeState ()) { + parser.Push (document.GetCharAt (i++)); + } + + //if nodes are incomplete, they won't get connected + //HACK: the only way to reconnect them is reflection + if (nodePath.Count > 1) { + for (int idx = 0; idx < nodePath.Count - 1; idx++) { + var node = nodePath [idx]; + if (node.Parent == null) { + var parent = nodePath [idx + 1]; + node.Parent = parent; + } + } + } + + return nodePath; + + bool InNameOrAttributeState () => + parser.CurrentState is XmlNameState + || parser.CurrentState is XmlAttributeState + || parser.CurrentState is XmlAttributeValueState; + + bool InRootOrClosingTagState () => + parser.CurrentState is XmlRootState + || parser.CurrentState is XmlNameState + || parser.CurrentState is XmlClosingTagState; + + int GetStateTag () => ((IXmlParserContext)parser).StateTag; + + bool NotInRootState () => !(parser.CurrentState is XmlRootState); + } + } + } + + internal static class XmlExtensions + { + public static DocumentRegion? TryGetNameRegion (this XObject xobject) + { + if (xobject is XElement element) { + var r = element.Region; + return new DocumentRegion (r.BeginLine, r.BeginColumn + 1, r.BeginLine, r.BeginColumn + 1 + element.Name.FullName.Length); + } + if (xobject is XClosingTag closingTag) { + var r = closingTag.Region; + return new DocumentRegion (r.BeginLine, r.BeginColumn + 2, r.BeginLine, r.BeginColumn + 2 + closingTag.Name.FullName.Length); + } + if (xobject is XAttribute attribute) { + var r = attribute.Region; + return new DocumentRegion (r.BeginLine, r.BeginColumn, r.BeginLine, r.BeginColumn + attribute.Name.FullName.Length); + } + return null; + } + + public static DocumentRegion GetAttributeValueRegion (this XAttribute att, IReadonlyTextDocument doc) + { + int endOffset = doc.LocationToOffset (att.Region.End) - 1; + int startOffset = endOffset - att.Value.Length; + return new DocumentRegion (doc.OffsetToLocation (startOffset), doc.OffsetToLocation (endOffset)); + } + + public static DocumentRegion GetAttributesRegion (this XElement element) + { + return new DocumentRegion (element.Attributes.First.Region.Begin, element.Attributes.Last.Region.End); + } + + public static bool ContainsOuter (this DocumentRegion region, DocumentLocation location) + { + return region.Begin <= location && location <= region.End; + } + } +} diff --git a/main/src/addins/Xml/MonoDevelop.Xml.csproj b/main/src/addins/Xml/MonoDevelop.Xml.csproj index 54121aa38d..5859c6720c 100644 --- a/main/src/addins/Xml/MonoDevelop.Xml.csproj +++ b/main/src/addins/Xml/MonoDevelop.Xml.csproj @@ -176,6 +176,7 @@ <Compile Include="Completion\XmlElementPath.cs" /> <Compile Include="Editor\XmlCommands.cs" /> <Compile Include="Editor\EncodedStringWriter.cs" /> + <Compile Include="Editor\XmlExpandSelectionHandler.cs" /> </ItemGroup> <ItemGroup> <None Include="packages.config" /> diff --git a/main/src/addins/Xml/Tests/ExpandSelectionTests.cs b/main/src/addins/Xml/Tests/ExpandSelectionTests.cs new file mode 100644 index 0000000000..5cc43fc238 --- /dev/null +++ b/main/src/addins/Xml/Tests/ExpandSelectionTests.cs @@ -0,0 +1,160 @@ +// +// ExpandSelectionTests.cs +// +// Author: +// Mikayla Hutchinson <m.j.hutchinson@gmail.com> +// +// Copyright (c) 2018 Microsoft Corp +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic;
+using System.Threading.Tasks;
+using MonoDevelop.Ide; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Extension; +using MonoDevelop.Xml.Editor; +using NUnit.Framework; + +namespace MonoDevelop.Xml.Tests +{ + [TestFixture] + public class ExpandSelectionTests : TextEditorExtensionTestBase + { + public static EditorExtensionTestData XmlContentData = new EditorExtensionTestData ( + fileName: "/a.xml", + language: "C#", + mimeType: "application/xml", + projectFileName: "test.csproj" + ); + + const string Document = @"<!-- this is +a comment--> +<foo hello=""hi"" goodbye=""bye""> + this is some text + <bar><baz thing=""done"" /> + <!--another comment--> + </bar> +</foo> +"; + const string CommentDoc = @"<!-- this is +a comment-->"; + + const string ElementFoo = @"<foo hello=""hi"" goodbye=""bye"">"; + + const string ElementWithBodyFoo = @"<foo hello=""hi"" goodbye=""bye""> + this is some text + <bar><baz thing=""done"" /> + <!--another comment--> + </bar> +</foo>"; + + const string AttributesFoo = @"hello=""hi"" goodbye=""bye"""; + + const string AttributeHello = @"hello=""hi"""; + + const string AttributeGoodbye = @"goodbye=""bye"""; + + const string BodyFoo = @" + this is some text + <bar><baz thing=""done"" /> + <!--another comment--> + </bar> +"; + const string ElementBar = @"<bar>"; + + const string ElementWithBodyBar = @"<bar><baz thing=""done"" /> + <!--another comment--> + </bar>"; + + const string BodyBar = @"<baz thing=""done"" /> + <!--another comment--> + "; + + const string ElementBaz= @"<baz thing=""done"" />"; + + const string AttributeThing = @"thing=""done"""; + + const string CommentBar = @"<!--another comment-->"; + + protected override EditorExtensionTestData GetContentData () => XmlContentData; + + protected override IEnumerable<TextEditorExtension> GetEditorExtensions () + { + yield return new XmlTextEditorExtension (); + } + + //args are line, col, then the expected sequence of expansions + [Test] + [TestCase (1, 2, CommentDoc)] + [TestCase (3, 2, "foo", ElementFoo, ElementWithBodyFoo)] + [TestCase (3, 3, "foo", ElementFoo, ElementWithBodyFoo)] + [TestCase (3, 15, "hi", AttributeHello, AttributesFoo, ElementFoo, ElementWithBodyFoo)] + [TestCase (3, 7, "hello", AttributeHello, AttributesFoo, ElementFoo, ElementWithBodyFoo)] + [TestCase (4, 7, BodyFoo, ElementWithBodyFoo)] + [TestCase (5, 22, "done", AttributeThing, ElementBaz, BodyBar, ElementWithBodyBar, BodyFoo, ElementWithBodyFoo)] + [TestCase (6, 12, CommentBar, BodyBar, ElementWithBodyBar, BodyFoo, ElementWithBodyFoo)] + public async Task TestExpandShrink (object[] args) + { + var loc = new DocumentLocation ((int)args [0], (int)args[1]); + using (var testCase = await SetupTestCase (Document)) { + var doc = testCase.Document; + doc.Editor.SetCaretLocation (loc); + var ext = doc.GetContent<BaseXmlEditorExtension> (); + + //check initial state + Assert.IsFalse (doc.Editor.IsSomethingSelected); + Assert.AreEqual (loc, doc.Editor.CaretLocation); + + //check expanding causes correct selections + for (int i = 2; i < args.Length; i++) { + ext.ExpandSelection (); + Assert.AreEqual (args [i], doc.Editor.SelectedText); + } + + //check entire doc is selected + ext.ExpandSelection (); + var sel = doc.Editor.SelectionRange; + Assert.AreEqual (0, sel.Offset); + Assert.AreEqual (Document.Length, sel.Length); + + //check expanding again does not change it + ext.ExpandSelection (); + Assert.AreEqual (0, sel.Offset); + Assert.AreEqual (Document.Length, sel.Length); + + //check shrinking causes correct selections + for (int i = args.Length - 1; i >= 2; i--) { + ext.ShrinkSelection (); + Assert.AreEqual (args [i], doc.Editor.SelectedText); + } + + //final shrink back to a caret + ext.ShrinkSelection (); + Assert.IsFalse (doc.Editor.IsSomethingSelected); + Assert.AreEqual (loc, doc.Editor.CaretLocation); + + //check shrinking again does not change it + ext.ShrinkSelection (); + Assert.IsFalse (doc.Editor.IsSomethingSelected); + Assert.AreEqual (loc, doc.Editor.CaretLocation); + } + } + } +} diff --git a/main/src/addins/Xml/Tests/MonoDevelop.Xml.Tests.csproj b/main/src/addins/Xml/Tests/MonoDevelop.Xml.Tests.csproj index 3484ee1fa2..95af752b1d 100644 --- a/main/src/addins/Xml/Tests/MonoDevelop.Xml.Tests.csproj +++ b/main/src/addins/Xml/Tests/MonoDevelop.Xml.Tests.csproj @@ -32,6 +32,14 @@ <Name>MonoDevelop.Xml</Name> <Private>False</Private> </ProjectReference> + <ProjectReference Include="..\..\..\..\tests\IdeUnitTests\IdeUnitTests.csproj"> + <Project>{F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}</Project> + <Name>IdeUnitTests</Name> + </ProjectReference> + <ProjectReference Include="..\..\..\..\tests\UnitTests\UnitTests.csproj"> + <Project>{1497D0A8-AFF1-4938-BC22-BE79B358BA5B}</Project> + <Name>UnitTests</Name> + </ProjectReference> </ItemGroup> <ItemGroup> <Reference Include="System" /> @@ -105,6 +113,7 @@ <Compile Include="Formatting\XmlFormatterTests.cs" /> <Compile Include="Schema\SchemaValidationTests.cs" /> <Compile Include="Utils\XsltTransformTest.cs" /> + <Compile Include="ExpandSelectionTests.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="..\schemas\XMLSchema.xsd"> |