Browse Source

Merge trunk into dotnet4.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/dotnet4@4303 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 16 years ago
parent
commit
e2973d98fe
  1. 6
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs
  2. 143
      src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs
  3. 3
      src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XamlBinding.Tests.csproj
  4. 4
      src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XmlTests.cs
  5. 417
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs
  6. 32
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs
  7. 6
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs
  8. 21
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs
  9. 2
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs
  10. 8
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs
  11. 295
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs
  12. 4
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.addin
  13. 1
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj
  14. 139
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs
  15. 16
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizer.cs
  16. 91
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItem.cs
  17. 39
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs
  18. 24
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs
  19. 114
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs
  20. 36
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  21. 18
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs
  22. 47
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/SharpDevelopCompletionWindow.cs
  23. 10
      src/AddIns/DisplayBindings/XmlEditor/Project/Src/FormatXmlCommand.cs
  24. 66
      src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCodeCompletionBinding.cs
  25. 4
      src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionDataProvider.cs
  26. 17
      src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItem.cs
  27. 37
      src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlFormattingStrategy.cs
  28. 21
      src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlParser.cs
  29. 6
      src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeView.cs
  30. 122
      src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs
  31. 5
      src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj
  32. 2
      src/AddIns/DisplayBindings/XmlEditor/Test/Parser/AttributeValueUnderCursorTests.cs
  33. 66
      src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln
  34. 19
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj
  35. 1
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml
  36. 177
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml.cs
  37. 38
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ExpandedNodes.cs
  38. 2
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs
  39. 2
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs
  40. 7
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedGraph.cs
  41. 79
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs
  42. 76
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNodeProperty.cs
  43. 25
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedPropertyEventArgs.cs
  44. 44
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/BoxDotFormatter.cs
  45. 82
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/DotFormatter.cs
  46. 39
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoDoubleFormatter.cs
  47. 13
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoEdgeRouter.cs
  48. 6
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoProcess.cs
  49. 68
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/RecordDotFormatter.cs
  50. 8
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeEdge.cs
  51. 36
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs
  52. 50
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs
  53. 2
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs
  54. 2
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs
  55. 4
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs
  56. 5
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs
  57. 133
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs
  58. 36
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs
  59. 13
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectProperty.cs
  60. 39
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphVisualizerViewContent.cs
  61. 12
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml
  62. 33
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml.cs
  63. 75
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs
  64. 92
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.Designer.cs
  65. 84
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.cs
  66. 4
      src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/TestPrograms/MemoryReadWrite.cs
  67. 6
      src/AddIns/Misc/UnitTesting/Test/Utils/MockProjectContent.cs
  68. 1
      src/Libraries/AvalonDock/AvalonDock.csproj
  69. 25
      src/Libraries/AvalonDock/DeserializationCallbackEventArgs.cs
  70. 8
      src/Libraries/AvalonDock/DockableContent.cs
  71. 6
      src/Libraries/AvalonDock/DockablePane.cs
  72. 140
      src/Libraries/AvalonDock/DockingManager.cs
  73. 24
      src/Libraries/AvalonDock/FloatingWindow.cs
  74. 2
      src/Libraries/AvalonDock/Properties/AssemblyInfo.cs
  75. 4
      src/Libraries/AvalonDock/ResizingPanel.cs
  76. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs
  77. 38
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs
  78. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindowBase.cs
  79. 5
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/InsightWindow.cs
  80. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/NewLineFinder.cs
  81. 28
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
  82. 2
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  83. 26
      src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditTextEditorAdapter.cs
  84. 24
      src/Main/Base/Project/Src/Editor/CodeCompletion/ICompletionListWindow.cs
  85. 62
      src/Main/Base/Project/Src/Editor/CodeCompletion/ICompletionWindow.cs
  86. 30
      src/Main/Base/Project/Src/Editor/CodeCompletion/IInsightWindow.cs
  87. 7
      src/Main/Base/Project/Src/Editor/ITextEditor.cs
  88. 2
      src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs
  89. 2
      src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskView.cs
  90. 7
      src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs
  91. 9
      src/Main/Base/Project/Src/TextEditor/Gui/TextEditorAdapter.cs
  92. 7
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs
  93. 7
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs
  94. 6
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ReflectionProjectContent.cs

6
src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs

@ -354,5 +354,11 @@ namespace PythonBinding.Tests.Utils
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public string AssemblyName {
get {
throw new NotImplementedException();
}
}
} }
} }

143
src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs

@ -18,85 +18,134 @@ namespace ICSharpCode.XamlBinding.Tests
public class UtilsTests public class UtilsTests
{ {
[Test] [Test]
public void XmlNamespacesForOffsetSimple() public void DiffTestSimple()
{ {
string xaml = File.ReadAllText("Test1.xaml"); string xaml = "<Test val1=\"Test\" />";
int offset = xaml.IndexOf("CheckBox") + "CheckBox ".Length; int offset = "<Test val1=\"Te".Length;
int expectedResult = offset - "<Test val1=\"".Length;
var expectedResult = new Dictionary<string, string> { int actualResult = Utils.GetOffsetFromValueStart(xaml, offset);
{"xmlns", "http://schemas.microsoft.com/netfx/2007/xaml/presentation"},
{"xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml"}
};
var result = Utils.GetXmlNamespacesForOffset(xaml, offset); Assert.AreEqual(expectedResult, actualResult);
}
[Test]
public void DiffTestSimple2()
{
string xaml = "<Test val1=\"Test\" />";
int offset = "<Test val1=\"".Length;
int expectedResult = offset - "<Test val1=\"".Length;
foreach (var p in result) int actualResult = Utils.GetOffsetFromValueStart(xaml, offset);
Debug.Print(p.Key + " " + p.Value);
Assert.AreEqual(expectedResult, result, "Is not equal"); Assert.AreEqual(expectedResult, actualResult);
} }
[Test] [Test]
public void XmlNamespacesForOffsetSimple2() public void GetOffsetTest1()
{ {
string xaml = File.ReadAllText("Test2.xaml"); string text = @"SharpDevelop uses the MSBuild
int offset = xaml.IndexOf("CheckBox") + "CheckBox ".Length; libraries for compilation. But when you compile a project
inside SharpDevelop, there's more going on than a
var expectedResult = new Dictionary<string, string> { simple call to MSBuild.";
{"xmlns", "http://schemas.microsoft.com/netfx/2007/xaml/presentation"},
{"xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml"},
{"xmlns:y", "clr-namespace:ICSharpCode.Profiler.Controls;assembly=ICSharpCode.Profiler.Controls"}
};
var result = Utils.GetXmlNamespacesForOffset(xaml, offset); int expected = 0;
int line = 1;
int col = 1;
foreach (var p in result) int result = Utils.GetOffsetFromFilePos(text, line, col);
Debug.Print(p.Key + " " + p.Value);
Assert.AreEqual(expectedResult, result, "Is not equal"); Assert.AreEqual(expected, result);
} }
[Test] [Test]
public void XmlNamespacesForOffsetComplex() public void GetOffsetTest2()
{ {
string xaml = File.ReadAllText("Test3.xaml"); string text = @"SharpDevelop uses the MSBuild
int offset = xaml.IndexOf("CheckBox") + "CheckBox ".Length; libraries for compilation. But when you compile a project
inside SharpDevelop, there's more going on than a
simple call to MSBuild.";
int expected = 4;
int line = 1;
int col = 5;
var expectedResult = new Dictionary<string, string> { int result = Utils.GetOffsetFromFilePos(text, line, col);
{"xmlns", "http://schemas.microsoft.com/netfx/2007/xaml/presentation"},
{"xmlns:x", "clr-namespace:ICSharpCode.Profiler.Controls;assembly=ICSharpCode.Profiler.Controls"} Assert.AreEqual(expected, result);
}; }
[Test]
public void GetOffsetTest3()
{
string text = @"SharpDevelop uses the MSBuild
libraries for compilation. But when you compile a project
inside SharpDevelop, there's more going on than a
simple call to MSBuild.";
var result = Utils.GetXmlNamespacesForOffset(xaml, offset); int expected = 0;
int line = 0;
int col = 5;
foreach (var p in result) int result = Utils.GetOffsetFromFilePos(text, line, col);
Debug.Print(p.Key + " " + p.Value);
Assert.AreEqual(expectedResult, result, "Is not equal"); Assert.AreEqual(expected, result);
} }
[Test] [Test]
public void DiffTestSimple() public void GetOffsetTest4()
{ {
string xaml = "<Test val1=\"Test\" />"; string text = @"SharpDevelop uses the MSBuild
int offset = "<Test val1=\"Te".Length; libraries for compilation. But when you compile a project
int expectedResult = offset - "<Test val1=\"".Length; inside SharpDevelop, there's more going on than a
simple call to MSBuild.";
int actualResult = Utils.GetOffsetFromValueStart(xaml, offset); int expected = @"SharpDevelop uses the MSBuild
libraries".Length - 1;
int line = 2;
int col = 10;
Assert.AreEqual(expectedResult, actualResult); int result = Utils.GetOffsetFromFilePos(text, line, col);
Assert.AreEqual(expected, result);
}
[Test]
public void GetOffsetTest5()
{
string text = @"SharpDevelop uses the MSBuild
libraries for compilation. But when you compile a project
inside SharpDevelop, there's more going on than a
simple call to MSBuild.";
int expected = text.Length;
int line = 10;
int col = 10;
int result = Utils.GetOffsetFromFilePos(text, line, col);
Assert.AreEqual(expected, result);
} }
[Test] [Test]
public void DiffTestSimple2() public void GetOffsetTest6()
{ {
string xaml = "<Test val1=\"Test\" />"; string text = @"SharpDevelop uses the MSBuild
int offset = "<Test val1=\"".Length; libraries for compilation. But when you compile a project
int expectedResult = offset - "<Test val1=\"".Length; inside SharpDevelop, there's more going on than a
simple call to MSBuild.";
int actualResult = Utils.GetOffsetFromValueStart(xaml, offset); int expected = @"SharpDevelop uses the MSBuild
libraries for compilation. But when you compile a project
inside SharpDevelop, there's more going on than a
simple".Length - 1;
Assert.AreEqual(expectedResult, actualResult); int line = 4;
int col = 7;
int result = Utils.GetOffsetFromFilePos(text, line, col);
Assert.AreEqual(expected, result);
} }
} }
} }

3
src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XamlBinding.Tests.csproj

@ -38,8 +38,7 @@
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<ItemGroup> <ItemGroup>
<Reference Include="nunit.framework"> <Reference Include="nunit.framework">
<HintPath>..\..\..\..\Tools\NUnit\nunit.framework.dll</HintPath> <HintPath>..\..\..\..\..\bin\Tools\NUnit\nunit.framework.dll</HintPath>
<Private>False</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core"> <Reference Include="System.Core">

4
src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XmlTests.cs

@ -66,6 +66,8 @@ namespace ICSharpCode.XamlBinding.Tests
int offset = "<Test val1=\"{Bin".Length; int offset = "<Test val1=\"{Bin".Length;
Assert.AreEqual(true, XmlParser.IsInsideAttributeValue(xaml, offset)); Assert.AreEqual(true, XmlParser.IsInsideAttributeValue(xaml, offset));
Assert.AreEqual("{Binding Value}", XmlParser.GetAttributeValueAtIndex(xaml, offset));
Assert.AreEqual("val1", XmlParser.GetAttributeNameAtIndex(xaml, offset));
} }
[Test] [Test]
@ -75,6 +77,8 @@ namespace ICSharpCode.XamlBinding.Tests
int offset = "<Test val1=\"{Binding Value, Path=".Length; int offset = "<Test val1=\"{Binding Value, Path=".Length;
Assert.AreEqual(true, XmlParser.IsInsideAttributeValue(xaml, offset)); Assert.AreEqual(true, XmlParser.IsInsideAttributeValue(xaml, offset));
Assert.AreEqual("{Binding Value, Path=Control}", XmlParser.GetAttributeValueAtIndex(xaml, offset));
Assert.AreEqual("val1", XmlParser.GetAttributeNameAtIndex(xaml, offset));
} }
} }
} }

417
src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs

@ -5,7 +5,7 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.SharpDevelop.Project;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -15,6 +15,7 @@ using System.Xml;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
using ICSharpCode.XmlEditor; using ICSharpCode.XmlEditor;
using LoggingService = ICSharpCode.Core.LoggingService; using LoggingService = ICSharpCode.Core.LoggingService;
@ -32,37 +33,93 @@ namespace ICSharpCode.XamlBinding
static readonly List<ICompletionItem> standardAttributes = new List<ICompletionItem> { static readonly List<ICompletionItem> standardAttributes = new List<ICompletionItem> {
new DefaultCompletionItem("xmlns:") new DefaultCompletionItem("xmlns:")
}; };
static readonly List<string> xamlNamespaceAttributes = new List<string> {
"Class", "ClassModifier", "FieldModifier", "Name", "Subclass", "TypeArguments", "Uid"
};
#endregion #endregion
public const string XamlNamespace = "http://schemas.microsoft.com/winfx/2006/xaml"; public const string XamlNamespace = "http://schemas.microsoft.com/winfx/2006/xaml";
public static XamlContext ResolveContext(ITextEditor editor, char typedValue) public static XamlContext ResolveContext(string text, string fileName, int line, int col)
{ {
string text = editor.Document.Text; int offset = Utils.GetOffsetFromFilePos(text, line, col);
int offset = editor.Caret.Offset;
XamlResolver resolver = new XamlResolver();
ParseInformation info = ParserService.GetParseInformation(editor.FileName); ParseInformation info = ParserService.GetParseInformation(fileName);
XmlElementPath path = XmlParser.GetActiveElementStartPathAtIndex(text, offset); XmlElementPath path = XmlParser.GetActiveElementStartPathAtIndex(text, offset);
string attribute = XmlParser.GetAttributeNameAtIndex(text, offset); string attribute = XmlParser.GetAttributeNameAtIndex(text, offset);
string attributeValue = XmlParser.GetAttributeValueAtIndex(text, offset); string attributeValue = XmlParser.GetAttributeValueAtIndex(text, offset);
bool inAttributeValue = XmlParser.IsInsideAttributeValue(text, offset); bool inAttributeValue = XmlParser.IsInsideAttributeValue(text, offset);
int offsetFromValueStart = Utils.GetOffsetFromValueStart(text, offset); int offsetFromValueStart = Utils.GetOffsetFromValueStart(text, offset);
ResolveResult rr = null;
AttributeValue value = null; AttributeValue value = null;
XamlExpressionContext cxt = new XamlExpressionContext(path, attribute, inAttributeValue); value = MarkupExtensionParser.ParseValue(attributeValue);
XamlContextDescription description = XamlContextDescription.InTag;
if (path == null || path.Elements.Count == 0) {
description = XamlContextDescription.None;
path = XmlParser.GetParentElementPath(text.Substring(0, offset));
} else {
int ltOffset = XmlParser.GetActiveElementStartIndex(text, offset);
if (ltOffset == -1)
description = XamlContextDescription.AtTag;
else {
string space = text.Substring(ltOffset + 1, offset - ltOffset - 1);
var last = path.Elements.LastOrDefault();
if (last != null && last.ToString().StartsWith(space, StringComparison.Ordinal))
description = XamlContextDescription.AtTag;
}
}
if (inAttributeValue)
description = XamlContextDescription.InAttributeValue;
if (value != null && !value.IsString)
description = XamlContextDescription.InMarkupExtension;
if (Utils.IsInsideXmlComment(text, offset))
description = XamlContextDescription.InComment;
Dictionary<string, string> xmlnsDefs = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(attribute)) { using (XmlTextReader reader = Utils.CreateReaderAtTarget(text, line, col)) {
rr = resolver.Resolve(new ExpressionResult(attribute, cxt) { Region = new DomRegion(1,1) }, info, text); xmlnsDefs.AddRange(reader.GetNamespacesInScope(XmlNamespaceScope.All));
} }
var context = new XamlContext() {
Description = description,
AttributeName = attribute,
AttributeValue = value,
RawAttributeValue = attributeValue,
ValueStartOffset = offsetFromValueStart,
Path = (path == null || path.Elements.Count == 0) ? null : path,
XmlnsDefinitions = xmlnsDefs,
ParseInformation = info
};
return context;
}
public static XamlCompletionContext ResolveCompletionContext(ITextEditor editor, char typedValue)
{
string text = editor.Document.Text;
int offset = editor.Caret.Offset;
ParseInformation info = ParserService.GetParseInformation(editor.FileName);
XmlElementPath path = XmlParser.GetActiveElementStartPathAtIndex(text, offset);
string attribute = XmlParser.GetAttributeNameAtIndex(text, offset);
string attributeValue = XmlParser.GetAttributeValueAtIndex(text, offset);
bool inAttributeValue = XmlParser.IsInsideAttributeValue(text, offset);
int offsetFromValueStart = Utils.GetOffsetFromValueStart(text, offset);
AttributeValue value = null;
value = MarkupExtensionParser.ParseValue(attributeValue); value = MarkupExtensionParser.ParseValue(attributeValue);
XamlContextDescription description = XamlContextDescription.InTag; XamlContextDescription description = XamlContextDescription.InTag;
if (path == null || path.Elements.Count == 0) { if (path == null || path.Elements.Count == 0) {
description = XamlContextDescription.AtTag; description = XamlContextDescription.None;
path = XmlParser.GetParentElementPath(text.Substring(0, offset)); path = XmlParser.GetParentElementPath(text.Substring(0, offset));
} else { } else {
int ltOffset = XmlParser.GetActiveElementStartIndex(text, offset); int ltOffset = XmlParser.GetActiveElementStartIndex(text, offset);
@ -71,7 +128,7 @@ namespace ICSharpCode.XamlBinding
else { else {
string space = text.Substring(ltOffset + 1, offset - ltOffset - 1); string space = text.Substring(ltOffset + 1, offset - ltOffset - 1);
var last = path.Elements.LastOrDefault(); var last = path.Elements.LastOrDefault();
if (last != null && last.ToString().Equals(space, StringComparison.Ordinal)) if (last != null && last.ToString().StartsWith(space, StringComparison.Ordinal))
description = XamlContextDescription.AtTag; description = XamlContextDescription.AtTag;
} }
} }
@ -85,47 +142,116 @@ namespace ICSharpCode.XamlBinding
if (Utils.IsInsideXmlComment(text, offset)) if (Utils.IsInsideXmlComment(text, offset))
description = XamlContextDescription.InComment; description = XamlContextDescription.InComment;
var context = new XamlContext() { Dictionary<string, string> xmlnsDefs = new Dictionary<string, string>();
using (XmlTextReader reader = Utils.CreateReaderAtTarget(text, editor.Caret.Line, editor.Caret.Column)) {
xmlnsDefs.AddRange(reader.GetNamespacesInScope(XmlNamespaceScope.All));
}
var context = new XamlCompletionContext() {
PressedKey = typedValue, PressedKey = typedValue,
Description = description, Description = description,
ResolvedExpression = rr,
AttributeName = attribute, AttributeName = attribute,
AttributeValue = value, AttributeValue = value,
RawAttributeValue = attributeValue, RawAttributeValue = attributeValue,
ValueStartOffset = offsetFromValueStart, ValueStartOffset = offsetFromValueStart,
Path = (path == null || path.Elements.Count == 0) ? null : path Path = (path == null || path.Elements.Count == 0) ? null : path,
XmlnsDefinitions = xmlnsDefs,
ParseInformation = info,
Editor = editor
}; };
LoggingService.Debug(context);
return context; return context;
} }
static List<ICompletionItem> CreateListForAttributeName(ParseInformation parseInfo, XamlExpressionContext context, string[] existingItems) static List<ICompletionItem> CreateListForAttributeName(XamlCompletionContext context, string[] existingItems)
{ {
if (context.ElementPath.Elements.Count == 0) QualifiedName lastElement = context.Path.Elements.LastOrDefault();
return null; XamlCompilationUnit cu = context.ParseInformation.BestCompilationUnit as XamlCompilationUnit;
QualifiedName lastElement = context.ElementPath.Elements[context.ElementPath.Elements.Count - 1];
XamlCompilationUnit cu = parseInfo.BestCompilationUnit as XamlCompilationUnit;
if (cu == null) if (cu == null)
return null; return null;
IReturnType rt = cu.CreateType(lastElement.Namespace, lastElement.Name); IReturnType rt = cu.CreateType(lastElement.Namespace, lastElement.Name.Trim('.'));
if (rt == null) if (rt == null)
return null; return null;
var list = new List<ICompletionItem>(); var list = new List<ICompletionItem>();
string xamlPrefix = Utils.GetXamlNamespacePrefix(context);
foreach (string item in xamlNamespaceAttributes) {
if (!existingItems.Contains(xamlPrefix + ":" + item))
list.Add(new XamlCompletionItem(xamlPrefix, XamlNamespace, item));
}
foreach (IProperty p in rt.GetProperties()) { foreach (IProperty p in rt.GetProperties()) {
if (p.IsPublic && p.CanSet && !existingItems.Contains(p.Name)) { if (p.IsPublic && p.CanSet && !existingItems.Contains(p.Name)) {
list.Add(new XamlCompletionItem(p)); list.Add(new XamlCodeCompletionItem(p));
} }
} }
foreach (IEvent e in rt.GetEvents()) { foreach (IEvent e in rt.GetEvents()) {
if (e.IsPublic && !existingItems.Contains(e.Name)) { if (e.IsPublic && !existingItems.Contains(e.Name)) {
list.Add(new XamlCompletionItem(e)); list.Add(new XamlCodeCompletionItem(e));
} }
} }
return list; return list;
} }
public static IEnumerable<ICompletionItem> CreateListForXmlnsCompletion(IProjectContent projectContent)
{
List<XmlnsCompletionItem> list = new List<XmlnsCompletionItem>();
foreach (IProjectContent content in projectContent.ReferencedContents) {
foreach (IAttribute att in content.GetAssemblyAttributes()) {
if (att.PositionalArguments.Count == 2
&& att.AttributeType.FullyQualifiedName == "System.Windows.Markup.XmlnsDefinitionAttribute") {
list.Add(new XmlnsCompletionItem(att.PositionalArguments[0] as string, true));
}
}
foreach (string @namespace in content.NamespaceNames) {
if (!string.IsNullOrEmpty(@namespace))
list.Add(new XmlnsCompletionItem(@namespace, content.AssemblyName));
}
}
foreach (string @namespace in projectContent.NamespaceNames) {
if (!string.IsNullOrEmpty(@namespace))
list.Add(new XmlnsCompletionItem(@namespace, false));
}
return list
.Distinct(new XmlnsEqualityComparer())
.OrderBy(item => item, new XmlnsComparer())
.Cast<ICompletionItem>();
}
sealed class XmlnsEqualityComparer : IEqualityComparer<XmlnsCompletionItem> {
public bool Equals(XmlnsCompletionItem x, XmlnsCompletionItem y)
{
return x.Namespace == y.Namespace && x.Assembly == y.Assembly;
}
public int GetHashCode(XmlnsCompletionItem obj)
{
return string.IsNullOrEmpty(obj.Assembly) ? obj.Namespace.GetHashCode() : obj.Namespace.GetHashCode() ^ obj.Assembly.GetHashCode();
}
}
sealed class XmlnsComparer : IComparer<XmlnsCompletionItem> {
public int Compare(XmlnsCompletionItem x, XmlnsCompletionItem y)
{
if (x.IsUrl && y.IsUrl)
return x.Namespace.CompareTo(y.Namespace);
if (x.IsUrl)
return -1;
if (y.IsUrl)
return 1;
if (x.Assembly == y.Assembly)
return x.Namespace.CompareTo(y.Namespace);
else
return x.Assembly.CompareTo(y.Assembly);
}
}
static bool IsReaderAtTarget(XmlTextReader r, int caretLine, int caretColumn) static bool IsReaderAtTarget(XmlTextReader r, int caretLine, int caretColumn)
{ {
if (r.LineNumber > caretLine) if (r.LineNumber > caretLine)
@ -136,18 +262,18 @@ namespace ICSharpCode.XamlBinding
return false; return false;
} }
public static IList<ICompletionItem> CreateListForElement(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn) public static IList<ICompletionItem> CreateListForElement(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn, bool addOpeningBrace)
{ {
var items = GetClassesFromContext(parseInfo, fileContent, caretLine, caretColumn); var items = GetClassesFromContext(parseInfo, fileContent, caretLine, caretColumn);
var result = new List<ICompletionItem>(); var result = new List<ICompletionItem>();
foreach (var ns in items) { foreach (var ns in items) {
result.AddRange(from c in ns.Value result.AddRange((from c in ns.Value
where (c.ClassType == ClassType.Class && where (c.ClassType == ClassType.Class &&
!c.IsAbstract && !c.IsStatic && !c.IsAbstract && !c.IsStatic &&
!c.ClassInheritanceTree.Any(b => b.FullyQualifiedName == "System.Attribute") && !c.ClassInheritanceTree.Any(b => b.FullyQualifiedName == "System.Attribute") &&
c.Methods.Any(m => m.IsConstructor && m.IsPublic)) c.Methods.Any(m => m.IsConstructor && m.IsPublic))
select (new XamlCompletionItem(c, ns.Key) as ICompletionItem) select (new XamlCodeCompletionItem(c, ns.Key, addOpeningBrace))).Cast<ICompletionItem>()
); );
} }
@ -156,48 +282,61 @@ namespace ICSharpCode.XamlBinding
public static IList<ICompletionItem> CreateListOfMarkupExtensions(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn) public static IList<ICompletionItem> CreateListOfMarkupExtensions(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn)
{ {
var list = CreateListForElement(parseInfo, fileContent, caretLine, caretColumn); var list = CreateListForElement(parseInfo, fileContent, caretLine, caretColumn, false);
var neededItems = list var neededItems = list
.Where(i => ((i as XamlCompletionItem).Entity as IClass).ClassInheritanceTree .Where(i => ((i as XamlCodeCompletionItem).Entity as IClass).ClassInheritanceTree
.Any(item => item.FullyQualifiedName == "System.Windows.Markup.MarkupExtension")) .Any(item => item.FullyQualifiedName == "System.Windows.Markup.MarkupExtension"))
.Select( .Select(
selItem => { selItem => {
var it = selItem as XamlCompletionItem; var it = selItem as XamlCodeCompletionItem;
string text = it.Text; string text = it.Text;
if (it.Text.EndsWith("Extension", StringComparison.Ordinal)) if (it.Text.EndsWith("Extension", StringComparison.Ordinal))
text = text.Remove(it.Text.Length - "Extension".Length); text = text.Remove(it.Text.Length - "Extension".Length);
return new XamlCompletionItem(text, it.Entity) as ICompletionItem; return new XamlCodeCompletionItem(it.Entity, text);
} }
); )
.Cast<ICompletionItem>();
return neededItems.ToList(); return neededItems.ToList();
} }
public static ICompletionItemList CreateListForContext(ITextEditor editor, XamlContext context) public static ICompletionItemList CreateListForContext(ITextEditor editor, XamlCompletionContext context)
{ {
XamlCompletionItemList list = new XamlCompletionItemList(); XamlCompletionItemList list = new XamlCompletionItemList();
ParseInformation info = ParserService.GetParseInformation(editor.FileName); ParseInformation info = ParserService.GetParseInformation(editor.FileName);
switch (context.Description) { switch (context.Description) {
case XamlContextDescription.None:
if (context.Forced) {
list.Items.AddRange(standardElements.Select(item => new DefaultCompletionItem("<" + item.Text)).Cast<ICompletionItem>());
list.Items.AddRange(CreateListForElement(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, true));
}
break;
case XamlContextDescription.AtTag: case XamlContextDescription.AtTag:
list.Items.AddRange(standardElements); list.Items.AddRange(standardElements);
list.Items.AddRange(CreateListForElement(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column)); if (editor.Document.GetCharAt(editor.Caret.Offset - 1) == '.' || context.PressedKey == '.') {
var loc = editor.Document.OffsetToPosition(Utils.GetParentElementStart(editor));
var existing = Utils.GetListOfExistingAttributeNames(editor.Document.Text, loc.Line, loc.Column);
list.Items.AddRange(CreateListForAttributeName(context, existing).RemoveEvents());
} else
list.Items.AddRange(CreateListForElement(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, false));
break; break;
case XamlContextDescription.InTag: case XamlContextDescription.InTag:
var existing = Utils.GetListOfExistingAttributeNames(editor.Document.Text, editor.Caret.Offset); var existingAttribs = Utils.GetListOfExistingAttributeNames(editor.Document.Text, editor.Caret.Line, editor.Caret.Column);
list.Items.AddRange(CreateListForAttributeName(info, new XamlExpressionContext(context.Path, null, false), existing)); list.Items.AddRange(CreateListForAttributeName(context, existingAttribs));
QualifiedName last = context.Path.Elements[context.Path.Elements.Count - 1]; QualifiedName last = context.Path.Elements[context.Path.Elements.Count - 1];
TypeResolveResult trr = new XamlResolver().Resolve(new ExpressionResult(last.Name, new XamlExpressionContext(context.Path, null, false)), info, editor.Document.Text) as TypeResolveResult; TypeResolveResult trr = new XamlResolver().Resolve(new ExpressionResult(last.Name, context), info, editor.Document.Text) as TypeResolveResult;
if (trr != null && trr.ResolvedType != null && trr.ResolvedType.GetUnderlyingClass() != null) { if (trr != null && trr.ResolvedType != null && trr.ResolvedType.GetUnderlyingClass() != null) {
if (trr.ResolvedType.GetUnderlyingClass().ClassInheritanceTree.Any(i => i.FullyQualifiedName == "System.Windows.DependencyObject")) { if (trr.ResolvedType.GetUnderlyingClass().ClassInheritanceTree.Any(i => i.FullyQualifiedName == "System.Windows.DependencyObject")) {
list.Items.AddRange(GetListOfAttachedProperties(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, existing)); list.Items.AddRange(GetListOfAttachedProperties(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, existingAttribs));
list.Items.AddRange(GetListOfAttachedEvents(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, existing)); list.Items.AddRange(GetListOfAttachedEvents(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, existingAttribs));
} }
} }
list.Items.AddRange(standardAttributes); list.Items.AddRange(standardAttributes);
break; break;
case XamlContextDescription.InAttributeValue: case XamlContextDescription.InAttributeValue:
@ -210,10 +349,24 @@ namespace ICSharpCode.XamlBinding
return list; return list;
} }
public static IEnumerable<IInsightItem> CreateMarkupExtensionInsight(XamlContext context, ParseInformation info, ITextEditor editor) static bool FilterCollectionAttributes(ICompletionItem item)
{
if (item is XamlCodeCompletionItem) {
var comItem = item as XamlCodeCompletionItem;
if (comItem.Entity is IProperty) {
var prop = comItem.Entity as IProperty;
var c = prop.ReturnType.GetUnderlyingClass();
return c != null && c.ClassInheritanceTree.Any(b => b.FullyQualifiedName == "System.Collections.IEnumerable");
}
}
return false;
}
public static IEnumerable<IInsightItem> CreateMarkupExtensionInsight(XamlCompletionContext context, ParseInformation info, ITextEditor editor)
{ {
var markup = GetInnermostMarkup(context.AttributeValue.ExtensionValue); var markup = Utils.GetInnermostMarkup(context.AttributeValue.ExtensionValue);
var trr = ResolveMarkupExtensionType(markup, info, editor, context.Path); var trr = ResolveMarkupExtensionType(markup, context);
if (trr != null) { if (trr != null) {
var ctors = trr.ResolvedType var ctors = trr.ResolvedType
@ -228,14 +381,14 @@ namespace ICSharpCode.XamlBinding
} }
} }
public static ICompletionItemList CreateMarkupExtensionCompletion(XamlContext context, ParseInformation info, ITextEditor editor) public static ICompletionItemList CreateMarkupExtensionCompletion(XamlCompletionContext context, ParseInformation info, ITextEditor editor)
{ {
var list = new XamlCompletionItemList(); var list = new XamlCompletionItemList();
var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset);
var markup = GetInnermostMarkup(context.AttributeValue.ExtensionValue); var markup = Utils.GetInnermostMarkup(context.AttributeValue.ExtensionValue);
var trr = ResolveMarkupExtensionType(markup, info, editor, path); var trr = ResolveMarkupExtensionType(markup, context);
if (trr == null) { if (trr == null) {
list.Items.AddRange(CreateListOfMarkupExtensions(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column)); list.Items.AddRange(CreateListOfMarkupExtensions(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column));
@ -259,11 +412,11 @@ namespace ICSharpCode.XamlBinding
{ {
var ctors = trr.ResolvedType.GetMethods().Where(m => m.IsConstructor && m.Parameters.Count >= markup.PositionalArguments.Count); var ctors = trr.ResolvedType.GetMethods().Where(m => m.IsConstructor && m.Parameters.Count >= markup.PositionalArguments.Count);
if (ctors.Any(ctor => ctor.Parameters.Count >= markup.PositionalArguments.Count)) { if (ctors.Any(ctor => ctor.Parameters.Count >= markup.PositionalArguments.Count)) {
list.Items.AddRange(trr.ResolvedType.GetProperties().Select(p => new XamlCompletionItem(p.Name + "=", p) as ICompletionItem)); list.Items.AddRange(trr.ResolvedType.GetProperties().Where(p => p.CanSet && p.IsPublic).Select(p => new XamlCodeCompletionItem(p, p.Name + "=")).Cast<ICompletionItem>());
} }
} }
static void DoPositionalArgsCompletion(XamlCompletionItemList list, XamlContext context, TypeResolveResult trr, ParseInformation info, ITextEditor editor) static void DoPositionalArgsCompletion(XamlCompletionItemList list, XamlCompletionContext context, TypeResolveResult trr, ParseInformation info, ITextEditor editor)
{ {
switch (trr.ResolvedType.FullyQualifiedName) { switch (trr.ResolvedType.FullyQualifiedName) {
case "System.Windows.Markup.ArrayExtension": case "System.Windows.Markup.ArrayExtension":
@ -272,12 +425,12 @@ namespace ICSharpCode.XamlBinding
break; break;
case "System.Windows.Markup.StaticExtension": case "System.Windows.Markup.StaticExtension":
if (context.AttributeValue.ExtensionValue.PositionalArguments.Count == 1 && context.PressedKey == ' ') break; if (context.AttributeValue.ExtensionValue.PositionalArguments.Count == 1 && context.PressedKey == ' ') break;
if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) DoStaticExtensionCompletion(list, context, info, editor); if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) DoStaticExtensionCompletion(list, context);
break; break;
case "System.Windows.Markup.TypeExtension": case "System.Windows.Markup.TypeExtension":
if (context.AttributeValue.ExtensionValue.PositionalArguments.Count == 1 && context.PressedKey == ' ') break; if (context.AttributeValue.ExtensionValue.PositionalArguments.Count == 1 && context.PressedKey == ' ') break;
if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) { if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) {
list.Items.AddRange(CreateListForElement(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column)); list.Items.AddRange(CreateListForElement(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, false));
AttributeValue selItem = context.AttributeValue.ExtensionValue.PositionalArguments.LastOrDefault(); AttributeValue selItem = context.AttributeValue.ExtensionValue.PositionalArguments.LastOrDefault();
if (selItem != null && selItem.IsString) { if (selItem != null && selItem.IsString) {
string s = selItem.StringValue; string s = selItem.StringValue;
@ -318,7 +471,7 @@ namespace ICSharpCode.XamlBinding
} }
} }
public static IEnumerable<ICompletionItem> MemberCompletion(ITextEditor editor, IReturnType type) public static IEnumerable<ICompletionItem> MemberCompletion(XamlCompletionContext context, IReturnType type)
{ {
if (type == null || type.GetUnderlyingClass() == null) if (type == null || type.GetUnderlyingClass() == null)
yield break; yield break;
@ -328,7 +481,7 @@ namespace ICSharpCode.XamlBinding
switch (c.ClassType) { switch (c.ClassType) {
case ClassType.Enum: case ClassType.Enum:
foreach (IField f in c.Fields) foreach (IField f in c.Fields)
yield return new XamlCompletionItem(f); yield return new XamlCodeCompletionItem(f);
break; break;
case ClassType.Struct: case ClassType.Struct:
if (c.FullyQualifiedName == "System.Boolean") { if (c.FullyQualifiedName == "System.Boolean") {
@ -339,60 +492,79 @@ namespace ICSharpCode.XamlBinding
case ClassType.Delegate: case ClassType.Delegate:
IMethod invoker = c.Methods.Where(method => method.Name == "Invoke").FirstOrDefault(); IMethod invoker = c.Methods.Where(method => method.Name == "Invoke").FirstOrDefault();
if (invoker != null) { if (invoker != null) {
var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); if (context.Path != null) {
if (path != null && path.Elements.Count > 0) { var item = context.Path.Elements.LastOrDefault();
var item = path.Elements[path.Elements.Count - 1]; var evt = ResolveAttribute(context.AttributeName, context) as IEvent;
string attribute = XmlParser.GetAttributeNameAtIndex(editor.Document.Text, editor.Caret.Offset); if (evt == null)
var e = ResolveAttribute(attribute, editor) as IEvent; break;
if (e == null)
int offset = XmlParser.GetActiveElementStartIndex(context.Editor.Document.Text, context.Editor.Caret.Offset);
if (offset == -1)
break; break;
string name = Utils.GetAttributeValue(editor.Document.Text, editor.Caret.Offset, "name");
yield return new NewEventCompletionItem(e, (string.IsNullOrEmpty(name)) ? item.Name : name);
foreach (var eventItem in CompletionDataHelper.AddMatchingEventHandlers(editor, invoker)) var loc = context.Editor.Document.OffsetToPosition(offset);
string prefix = Utils.GetXamlNamespacePrefix(context);
string name = Utils.GetAttributeValue(context.Editor.Document.Text, loc.Line, loc.Column + 1, "name");
if (string.IsNullOrEmpty(name))
name = Utils.GetAttributeValue(context.Editor.Document.Text, loc.Line, loc.Column + 1, (string.IsNullOrEmpty(prefix) ? "" : prefix + ":") + "name");
yield return new NewEventCompletionItem(evt, (string.IsNullOrEmpty(name)) ? item.Name : name);
foreach (var eventItem in CompletionDataHelper.AddMatchingEventHandlers(context.Editor, invoker))
yield return eventItem; yield return eventItem;
} }
} }
break; break;
} }
switch (c.FullyQualifiedName) {
case "System.Windows.Media.Brush":
foreach (var item in typeof(System.Windows.Media.Brushes).GetProperties()) {
yield return new DefaultCompletionItem(item.Name);
}
break;
case "System.Windows.Media.Color":
foreach (var item in typeof(System.Windows.Media.Colors).GetProperties()) {
yield return new DefaultCompletionItem(item.Name);
}
break;
}
} }
static IEntity ResolveAttribute(string attribute, ITextEditor editor) static IEntity ResolveAttribute(string attribute, XamlCompletionContext context)
{ {
XamlResolver resolver = new XamlResolver(); XamlResolver resolver = new XamlResolver();
var exp = new ExpressionResult(attribute, context);
var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); var mrr = resolver.Resolve(exp, context.ParseInformation, context.Editor.Document.Text) as MemberResolveResult;
var exp = new ExpressionResult(attribute, new XamlExpressionContext(path, attribute, false));
var info = ParserService.GetParseInformation(editor.FileName);
var mrr = resolver.Resolve(exp, info, editor.Document.Text) as MemberResolveResult;
return mrr.ResolvedMember; return mrr.ResolvedMember;
} }
static void DoStaticExtensionCompletion(XamlCompletionItemList list, XamlContext context, ParseInformation info, ITextEditor editor) static void DoStaticExtensionCompletion(XamlCompletionItemList list, XamlCompletionContext context)
{ {
AttributeValue selItem = context.AttributeValue.ExtensionValue.PositionalArguments.LastOrDefault(); AttributeValue selItem = context.AttributeValue.ExtensionValue.PositionalArguments.LastOrDefault();
if (context.PressedKey == '.') { if (context.PressedKey == '.') {
if (selItem != null && selItem.IsString) { if (selItem != null && selItem.IsString) {
var rr = ResolveStringValue(selItem.StringValue, context.Path, info, editor) as TypeResolveResult; var rr = ResolveStringValue(selItem.StringValue, context) as TypeResolveResult;
if (rr != null) if (rr != null)
list.Items.AddRange(MemberCompletion(editor, rr.ResolvedType)); list.Items.AddRange(MemberCompletion(context, rr.ResolvedType));
} }
} else { } else {
if (selItem != null && selItem.IsString) { if (selItem != null && selItem.IsString) {
int index = selItem.StringValue.IndexOf('.'); int index = selItem.StringValue.IndexOf('.');
string s = (index > -1) ? selItem.StringValue.Substring(0, index) : selItem.StringValue; string s = (index > -1) ? selItem.StringValue.Substring(0, index) : selItem.StringValue;
var rr = ResolveStringValue(s, context.Path, info, editor) as TypeResolveResult; var rr = ResolveStringValue(s, context) as TypeResolveResult;
if (rr != null) { if (rr != null) {
list.Items.AddRange(MemberCompletion(editor, rr.ResolvedType)); list.Items.AddRange(MemberCompletion(context, rr.ResolvedType));
list.PreselectionLength = selItem.StringValue.Length - index - 1; list.PreselectionLength = selItem.StringValue.Length - index - 1;
list.SuggestedItem = list.Items.FirstOrDefault(item => item.Text.StartsWith(selItem.StringValue.Substring(index + 1), StringComparison.OrdinalIgnoreCase)); list.SuggestedItem = list.Items.FirstOrDefault(item => item.Text.StartsWith(selItem.StringValue.Substring(index + 1), StringComparison.OrdinalIgnoreCase));
} else } else
DoStaticTypeCompletion(selItem, list, info, editor); DoStaticTypeCompletion(selItem, list, context.ParseInformation, context.Editor);
} else { } else {
DoStaticTypeCompletion(selItem, list, info, editor); DoStaticTypeCompletion(selItem, list, context.ParseInformation, context.Editor);
} }
} }
} }
@ -401,7 +573,9 @@ namespace ICSharpCode.XamlBinding
{ {
var items = GetClassesFromContext(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column); var items = GetClassesFromContext(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column);
foreach (var ns in items) { foreach (var ns in items) {
list.Items.AddRange(ns.Value.Where(c => c.Fields.Any(f => f.IsStatic) || c.Properties.Any(p => p.IsStatic)).Select(c => new XamlCompletionItem(c, ns.Key) as ICompletionItem)); list.Items.AddRange(ns.Value.Where(c => c.Fields.Any(f => f.IsStatic) || c.Properties.Any(p => p.IsStatic))
.Select(c => new XamlCodeCompletionItem(c, ns.Key, false))
.Cast<ICompletionItem>());
} }
if (selItem != null && selItem.IsString) { if (selItem != null && selItem.IsString) {
string s = selItem.StringValue; string s = selItem.StringValue;
@ -410,10 +584,10 @@ namespace ICSharpCode.XamlBinding
} }
} }
static ResolveResult ResolveStringValue(string value, XmlElementPath path, ParseInformation info, ITextEditor editor) static ResolveResult ResolveStringValue(string value, XamlCompletionContext context)
{ {
var resolver = new XamlResolver(); var resolver = new XamlResolver();
var rr = resolver.Resolve(new ExpressionResult(value, new XamlExpressionContext(path, string.Empty, false)), info, editor.Document.Text); var rr = resolver.Resolve(new ExpressionResult(value, context), context.ParseInformation, context.Editor.Document.Text);
return rr; return rr;
} }
@ -421,33 +595,22 @@ namespace ICSharpCode.XamlBinding
{ {
return null; return null;
} }
public static MarkupExtensionInfo GetInnermostMarkup(MarkupExtensionInfo markup)
{
var lastPair = markup.NamedArguments.LastOrDefault();
var last = markup.PositionalArguments.LastOrDefault();
if (markup.NamedArguments.Count > 0)
last = lastPair.Value;
if (last != null) {
if (!last.IsString) {
return GetInnermostMarkup(last.ExtensionValue);
}
}
return markup;
}
static TypeResolveResult ResolveMarkupExtensionType(MarkupExtensionInfo markup, ParseInformation info, ITextEditor editor, XmlElementPath path) public static TypeResolveResult ResolveMarkupExtensionType(MarkupExtensionInfo markup, XamlCompletionContext context)
{ {
XamlResolver resolver = new XamlResolver(); XamlResolver resolver = new XamlResolver();
TypeResolveResult trr = resolver.Resolve(new ExpressionResult(markup.ExtensionType, new XamlExpressionContext(path, null, false)), info, editor.Document.Text) as TypeResolveResult; TypeResolveResult trr = resolver.Resolve(new ExpressionResult(markup.ExtensionType, context), context.ParseInformation, context.Editor.Document.Text) as TypeResolveResult;
if (trr == null) trr = resolver.Resolve(new ExpressionResult(markup.ExtensionType + "Extension", new XamlExpressionContext(path, null, false)), info, editor.Document.Text) as TypeResolveResult; if (trr == null) trr = resolver.Resolve(new ExpressionResult(markup.ExtensionType + "Extension", context), context.ParseInformation, context.Editor.Document.Text) as TypeResolveResult;
return trr; return trr;
} }
public static TypeResolveResult ResolveType(string name, XamlCompletionContext context)
{
return new XamlResolver()
.Resolve(new ExpressionResult(name, context), context.ParseInformation, context.Editor.Document.Text) as TypeResolveResult;
}
public static IEnumerable<ICompletionItem> AddMatchingEventHandlers(ITextEditor editor, IMethod delegateInvoker) public static IEnumerable<ICompletionItem> AddMatchingEventHandlers(ITextEditor editor, IMethod delegateInvoker)
{ {
ParseInformation p = ParserService.GetParseInformation(editor.FileName); ParseInformation p = ParserService.GetParseInformation(editor.FileName);
@ -473,7 +636,7 @@ namespace ICSharpCode.XamlBinding
break; break;
} }
if (equal) { if (equal) {
yield return new XamlCompletionItem(m); yield return new XamlCodeCompletionItem(m);
} }
} }
} }
@ -491,15 +654,22 @@ namespace ICSharpCode.XamlBinding
return result; return result;
} }
static IDictionary<string, IEnumerable<IClass>> GetClassesFromContext(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn) static string GetPrefixForNamespace(string @namespace, string fileContent, int caretLine, int caretColumn)
{ {
using (XmlTextReader r = new XmlTextReader(new StringReader(fileContent))) { using (XmlTextReader r = Utils.CreateReaderAtTarget(fileContent, caretLine, caretColumn)) {
try { foreach (var item in r.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml)) {
r.WhitespaceHandling = WhitespaceHandling.Significant; if (item.Value == @namespace) {
// move reader to correct position return item.Key;
while (r.Read() && !IsReaderAtTarget(r, caretLine, caretColumn)) { } }
} catch (XmlException) {} }
return string.Empty;
}
}
static IDictionary<string, IEnumerable<IClass>> GetClassesFromContext(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn)
{
using (XmlTextReader r = Utils.CreateReaderAtTarget(fileContent, caretLine, caretColumn)) {
IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent; IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent;
var result = new Dictionary<string, IEnumerable<IClass>>(); var result = new Dictionary<string, IEnumerable<IClass>>();
@ -514,15 +684,8 @@ namespace ICSharpCode.XamlBinding
static List<ICompletionItem> GetListOfAttachedProperties(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn, string[] existingItems) static List<ICompletionItem> GetListOfAttachedProperties(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn, string[] existingItems)
{ {
using (XmlTextReader r = new XmlTextReader(new StringReader(fileContent))) { using (XmlTextReader r = Utils.CreateReaderAtTarget(fileContent, caretLine, caretColumn)) {
try { List<ICompletionItem> result = new List<ICompletionItem>();
r.WhitespaceHandling = WhitespaceHandling.Significant;
// move reader to correct position
while (r.Read() && !IsReaderAtTarget(r, caretLine, caretColumn)) { }
}
catch (XmlException) {
}
var result = new List<ICompletionItem>();
IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent; IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent;
foreach (var ns in r.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml)) { foreach (var ns in r.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml)) {
@ -561,10 +724,11 @@ namespace ICSharpCode.XamlBinding
string name = (!string.IsNullOrEmpty(ns.Key)) ? ns.Key + ":" : ""; string name = (!string.IsNullOrEmpty(ns.Key)) ? ns.Key + ":" : "";
string property = item.Name.Remove(item.Name.Length - "Property".Length); string property = item.Name.Remove(item.Name.Length - "Property".Length);
name += c.Name + "." + item.Name.Remove(item.Name.Length - "Property".Length); name += c.Name + "." + item.Name.Remove(item.Name.Length - "Property".Length);
return new XamlCompletionItem(name, new DefaultProperty(c, property) { ReturnType = GetAttachedPropertyType(item, c) } ) as ICompletionItem; return new XamlCodeCompletionItem(new DefaultProperty(c, property) { ReturnType = GetAttachedPropertyType(item, c) }, name);
} }
) )
.Where(item => !existingItems.Any(str => str == item.Text)) .Where(item => !existingItems.Any(str => str == item.Text))
.Cast<ICompletionItem>()
); );
} }
} }
@ -611,14 +775,15 @@ namespace ICSharpCode.XamlBinding
result.AddRange(attachedEvents result.AddRange(attachedEvents
.Select( .Select(
item => new XamlCompletionItem( item => new XamlCodeCompletionItem(
(string.IsNullOrEmpty(ns.Key) ? "" : ns.Key + ":") + c.Name + "." + item.Name.Remove(item.Name.Length - "Event".Length), new DefaultEvent(c, GetEventNameFromField(item)) {
new DefaultEvent(c, GetEventNameFromField(item)) { ReturnType = GetAttachedEventDelegateType(item, c)
ReturnType = GetAttachedEventDelegateType(item, c) },
} (string.IsNullOrEmpty(ns.Key) ? "" : ns.Key + ":") + c.Name + "." + item.Name.Remove(item.Name.Length - "Event".Length)
) as ICompletionItem )
) )
.Where(item => !existingItems.Any(str => str == item.Text)) .Where(item => !existingItems.Any(str => str == item.Text))
.Cast<ICompletionItem>()
); );
} }
} }

32
src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs

@ -5,10 +5,13 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.SharpDevelop;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
using ICSharpCode.XmlEditor; using ICSharpCode.XmlEditor;
namespace ICSharpCode.XamlBinding namespace ICSharpCode.XamlBinding
@ -36,15 +39,26 @@ namespace ICSharpCode.XamlBinding
return false; return false;
} }
public static QualifiedName LastOrDefault(this QualifiedNameCollection collection) public static IEnumerable<ICompletionItem> RemoveEvents(this IEnumerable<ICompletionItem> list)
{ {
if (collection == null) foreach (var item in list) {
throw new ArgumentNullException("collection"); if (item is XamlCodeCompletionItem) {
var comItem = item as XamlCodeCompletionItem;
if (collection.Count > 0) if (!(comItem.Entity is IEvent))
return collection[collection.Count - 1]; yield return item;
} else yield return item;
return null; }
}
public static IEnumerable<ICompletionItem> RemoveProperties(this IEnumerable<ICompletionItem> list)
{
foreach (var item in list) {
if (item is XamlCodeCompletionItem) {
var comItem = item as XamlCodeCompletionItem;
if (!(comItem.Entity is IProperty))
yield return item;
} else yield return item;
}
} }
} }
} }

6
src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs

@ -20,9 +20,11 @@ namespace ICSharpCode.XamlBinding
public string ExtensionType { get; set; } public string ExtensionType { get; set; }
public IList<AttributeValue> PositionalArguments { get; private set; } public IList<AttributeValue> PositionalArguments { get; private set; }
public IDictionary<string, AttributeValue> NamedArguments { get; private set; } public IDictionary<string, AttributeValue> NamedArguments { get; private set; }
public int StartOffset { get; set; }
public MarkupExtensionInfo() public MarkupExtensionInfo()
: this(string.Empty, new List<AttributeValue>(), new Dictionary<string, AttributeValue>()) : this(string.Empty, new List<AttributeValue>(), new Dictionary<string, AttributeValue>(StringComparer.OrdinalIgnoreCase))
{ {
} }
@ -39,6 +41,8 @@ namespace ICSharpCode.XamlBinding
string stringValue; string stringValue;
MarkupExtensionInfo extensionValue; MarkupExtensionInfo extensionValue;
public int StartOffset { get; set; }
public bool IsString { public bool IsString {
get { return stringValue != null; } get { return stringValue != null; }
} }

21
src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs

@ -19,22 +19,28 @@ namespace ICSharpCode.XamlBinding
var tokenizer = new MarkupExtensionTokenizer(text); var tokenizer = new MarkupExtensionTokenizer(text);
string argumentName = null; string argumentName = null;
int namedArgsStart = 0;
var token = tokenizer.NextToken(); var token = tokenizer.NextToken();
while (token.Kind != MarkupExtensionTokenKind.EndOfFile) { while (token.Kind != MarkupExtensionTokenKind.EndOfFile) {
switch (token.Kind) { switch (token.Kind) {
case MarkupExtensionTokenKind.TypeName: case MarkupExtensionTokenKind.TypeName:
info.ExtensionType = token.Value; info.ExtensionType = token.Value;
info.StartOffset = token.StartOffset;
break; break;
case MarkupExtensionTokenKind.MemberName: case MarkupExtensionTokenKind.MemberName:
// if there is an open member without a value add the member name
if (argumentName != null)
info.NamedArguments.Add(argumentName, new AttributeValue(string.Empty));
argumentName = token.Value; argumentName = token.Value;
namedArgsStart = token.StartOffset;
break; break;
case MarkupExtensionTokenKind.String: case MarkupExtensionTokenKind.String:
if (argumentName != null) { if (argumentName != null) {
info.NamedArguments.Add(argumentName, ParseValue(token.Value)); info.NamedArguments.Add(argumentName, ParseValue(token.Value, namedArgsStart));
argumentName = null; argumentName = null;
} else { } else {
info.PositionalArguments.Add(ParseValue(token.Value)); info.PositionalArguments.Add(ParseValue(token.Value, token.StartOffset));
} }
break; break;
} }
@ -48,14 +54,19 @@ namespace ICSharpCode.XamlBinding
} }
public static AttributeValue ParseValue(string text) public static AttributeValue ParseValue(string text)
{
return ParseValue(text, 0);
}
public static AttributeValue ParseValue(string text, int offset)
{ {
if (string.IsNullOrEmpty(text)) if (string.IsNullOrEmpty(text))
return new AttributeValue(string.Empty); return new AttributeValue(string.Empty) { StartOffset = offset };
if (text.StartsWith("{", StringComparison.OrdinalIgnoreCase)) if (text.StartsWith("{", StringComparison.OrdinalIgnoreCase))
return new AttributeValue(Parse(text)); return new AttributeValue(Parse(text)) { StartOffset = offset };
else else
return new AttributeValue(text); return new AttributeValue(text) { StartOffset = offset };
} }
} }
} }

2
src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs

@ -14,6 +14,8 @@ namespace ICSharpCode.XamlBinding
public MarkupExtensionTokenKind Kind { get; private set; } public MarkupExtensionTokenKind Kind { get; private set; }
public string Value { get; private set; } public string Value { get; private set; }
public int StartOffset { get; set; }
public MarkupExtensionToken(MarkupExtensionTokenKind kind, string value) public MarkupExtensionToken(MarkupExtensionTokenKind kind, string value)
{ {
this.Kind = kind; this.Kind = kind;

8
src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs

@ -27,6 +27,8 @@ namespace ICSharpCode.XamlBinding
string text; string text;
int pos; int pos;
int startPos;
Queue<MarkupExtensionToken> tokens = new Queue<MarkupExtensionToken>(); Queue<MarkupExtensionToken> tokens = new Queue<MarkupExtensionToken>();
/// <summary> /// <summary>
@ -51,7 +53,7 @@ namespace ICSharpCode.XamlBinding
void AddToken(MarkupExtensionTokenKind kind, string val) void AddToken(MarkupExtensionTokenKind kind, string val)
{ {
tokens.Enqueue(new MarkupExtensionToken(kind, val)); tokens.Enqueue(new MarkupExtensionToken(kind, val) { StartOffset = startPos });
} }
void ParseBeginning() void ParseBeginning()
@ -75,16 +77,20 @@ namespace ICSharpCode.XamlBinding
case '}': case '}':
AddToken(MarkupExtensionTokenKind.CloseBrace, "}"); AddToken(MarkupExtensionTokenKind.CloseBrace, "}");
pos++; pos++;
startPos = pos;
break; break;
case '=': case '=':
AddToken(MarkupExtensionTokenKind.Equals, "="); AddToken(MarkupExtensionTokenKind.Equals, "=");
pos++; pos++;
startPos = pos;
break; break;
case ',': case ',':
AddToken(MarkupExtensionTokenKind.Comma, ","); AddToken(MarkupExtensionTokenKind.Comma, ",");
pos++; pos++;
startPos = pos;
break; break;
default: default:
startPos = pos;
MembernameOrString(); MembernameOrString();
break; break;
} }

295
src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs

@ -10,9 +10,11 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.XmlEditor; using ICSharpCode.XmlEditor;
namespace ICSharpCode.XamlBinding namespace ICSharpCode.XamlBinding
@ -22,129 +24,40 @@ namespace ICSharpCode.XamlBinding
/// </summary> /// </summary>
public static class Utils public static class Utils
{ {
public static bool HasMatchingEndTag(string tagname, string text, int offset) internal static bool IsReaderAtTarget(XmlTextReader r, int line, int col)
{ {
int index = XmlParser.GetActiveElementStartIndex(text, offset); if (r.LineNumber > line)
if (index == -1) return true;
else if (r.LineNumber == line)
return r.LinePosition >= col;
else
return false; return false;
text = text.Substring(index);
var path = XmlParser.GetActiveElementStartPathAtIndex(text, index);
XmlReader reader = XmlTextReader.Create(new StringReader(text));
int startTags = 0;
try {
while (reader.Read()) {
switch (reader.NodeType) {
case XmlNodeType.Element:
if (!reader.IsEmptyElement)
startTags++;
break;
case XmlNodeType.EndElement:
startTags--;
if (startTags == 0 && tagname == reader.Name) {
return true;
}
break;
}
}
} catch (XmlException e) {
Debug.Print(e.ToString());
return false;
}
return false;
} }
public static string GetAttributeValue(string text, int offset, string name) public static string GetAttributeValue(string text, int line, int col, string name)
{ {
text = SimplifyToSingleElement(text, offset, "Test"); try {
XmlTextReader reader = new XmlTextReader(new StringReader(text));
if (text == null) reader.XmlResolver = null;
return null;
XmlReader reader = XmlTextReader.Create(new StringReader(text));
try {
reader.ReadToFollowing("Test");
while (reader.Read() && !IsReaderAtTarget(reader, line, col)) { }
if (!reader.MoveToFirstAttribute()) if (!reader.MoveToFirstAttribute())
return null; return null;
do { do {
LoggingService.Debug("name: " + reader.Name + " value: " + reader.Value); LoggingService.Debug("name: " + reader.Name + " value: " + reader.Value);
int start = reader.Name.IndexOf(':') + 1; string plainName = reader.Name.ToUpperInvariant();
string plainName = reader.Name.Substring(start, reader.Name.Length - start).ToUpperInvariant();
if (plainName == name.ToUpperInvariant()) if (plainName == name.ToUpperInvariant())
return reader.Value; return reader.Value;
} while (reader.MoveToNextAttribute()); } while (reader.MoveToNextAttribute());
} catch (XmlException) { } } catch (XmlException e) {
Debug.Print(e.ToString());
return null;
}
public static Dictionary<string, string> GetXmlNamespacesForOffset(string fileContent, int offset)
{
if (fileContent == null)
throw new ArgumentNullException("fileContent");
if (offset < 0 || offset > fileContent.Length)
throw new ArgumentOutOfRangeException("offset", offset, "Value must be between 0 and " + fileContent.Length);
var map = new Dictionary<string, string>();
int endIndex = fileContent.IndexOfAny(new char[] {'<', '>'}, offset);
if (endIndex > -1)
fileContent = fileContent.Substring(0, endIndex + 1);
int lastWhiteSpacePos = fileContent.LastIndexOfAny(new char[] {' ', '\t', '\n', '\r'});
bool inDouble = false, inSingle = false;
for (int i = 0; i < fileContent.Length; i++) {
if (fileContent[i] == '"' && !inSingle)
inDouble = !inDouble;
if (fileContent[i] == '\'' && !inDouble)
inSingle = !inSingle;
if (fileContent[i] == '>') {
int lastDelimiterPos = fileContent.Substring(0, i + 1).LastIndexOfAny(new char[] {'>', '/'});
if (inDouble) {
fileContent.Insert(lastDelimiterPos, "\"");
i++;
inDouble = false;
}
if (inSingle) {
fileContent.Insert(lastDelimiterPos, "'");
inSingle = false;
i++;
}
}
}
fileContent = fileContent.Replace("<?", " ").Replace("?>", " ").Replace("<", " ").Replace("/>", " ").Replace(">", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " ");
while (fileContent.Contains(" "))
fileContent = fileContent.Replace(" ", " ");
fileContent = fileContent.Replace("= \"", "=\"");
fileContent = fileContent.Replace(" =\"", "=\"");
Debug.Print(fileContent);
string[] data = fileContent.Split(' ');
var filter1 = data.Where(s => s.StartsWith("xmlns"));
foreach (string item in filter1) {
string[] parts = item.Split(new char[] {'='}, 2);
if (parts.Length == 2) {
if (map.ContainsKey(parts[0])) // replace namespace with new one
map.Remove(parts[0]);
map.Add(parts[0], parts[1].Trim('"', '\''));
}
} }
return map; return null;
} }
public static int GetOffsetFromValueStart(string xaml, int offset) public static int GetOffsetFromValueStart(string xaml, int offset)
@ -162,28 +75,29 @@ namespace ICSharpCode.XamlBinding
return offset - start - 1; return offset - start - 1;
} }
public static string[] GetListOfExistingAttributeNames(string text, int offset) public static string[] GetListOfExistingAttributeNames(string text, int line, int col)
{ {
List<string> list = new List<string>(); List<string> list = new List<string>();
text = SimplifyToSingleElement(text, offset, "Test");
if (text == null) if (text == null)
return list.ToArray(); return list.ToArray();
XmlReader reader = XmlTextReader.Create(new StringReader(text)); using (XmlReader reader = CreateReaderAtTarget(text, line, col)) {
try {
if (!reader.MoveToFirstAttribute())
return list.ToArray();
do {
LoggingService.Debug("name: " + reader.Name + " value: " + reader.Value);
list.Add(reader.Name);
} while (reader.MoveToNextAttribute());
} catch (XmlException e) {
Debug.Print(e.ToString());
}
}
try { foreach (var item in list)
reader.ReadToFollowing("Test"); Debug.Print(item);
if (!reader.MoveToFirstAttribute())
return list.ToArray();
do {
LoggingService.Debug("name: " + reader.Name + " value: " + reader.Value);
list.Add(reader.Name);
} while (reader.MoveToNextAttribute());
} catch (XmlException) { }
return list.ToArray(); return list.ToArray();
} }
@ -203,30 +117,15 @@ namespace ICSharpCode.XamlBinding
return startIndex; return startIndex;
} }
static string SimplifyToSingleElement(string text, int offset, string name)
{
int index = XmlParser.GetActiveElementStartIndex(text, offset);
if (index == -1) return null;
index = text.IndexOf(' ', index);
if (index == -1) return null;
text = text.Substring(index);
int endIndex = text.IndexOfAny(new char[] { '<', '>' });
if (endIndex == -1) return null;
text = text.Substring(0, endIndex).Trim(' ', '\t', '\n', '\r', '/');
LoggingService.Debug("text: '" + text + "'");
text = "<" + name + " " + text + " />";
return text;
}
public static string GetXamlNamespacePrefix(string xaml, int offset) static char[] whitespace = new char[] {' ', '\t', '\n', '\r'};
public static string GetXamlNamespacePrefix(XamlContext context)
{ {
var list = Utils.GetXmlNamespacesForOffset(xaml, offset); var item = context.XmlnsDefinitions.FirstOrDefault(i => i.Value == CompletionDataHelper.XamlNamespace);
var item = list.FirstOrDefault(i => i.Value == CompletionDataHelper.XamlNamespace);
if (item.Key != null)
if (item.Key.StartsWith("xmlns:", StringComparison.OrdinalIgnoreCase)) return item.Key;
return item.Key.Substring("xmlns:".Length);
return string.Empty; return string.Empty;
} }
@ -237,7 +136,7 @@ namespace ICSharpCode.XamlBinding
if (offset < 0) if (offset < 0)
throw new ArgumentOutOfRangeException("offset", offset, "Value must be between 0 and " + (xaml.Length - 1)); throw new ArgumentOutOfRangeException("offset", offset, "Value must be between 0 and " + (xaml.Length - 1));
if (offset >= xaml.Length) if (offset >= xaml.Length && offset > 0)
offset = xaml.Length - 1; offset = xaml.Length - 1;
string interestingPart = xaml.Substring(0, offset); string interestingPart = xaml.Substring(0, offset);
@ -247,5 +146,113 @@ namespace ICSharpCode.XamlBinding
return interestingPart.LastIndexOf("<!--", StringComparison.OrdinalIgnoreCase) != -1; return interestingPart.LastIndexOf("<!--", StringComparison.OrdinalIgnoreCase) != -1;
} }
public static int GetOffsetFromFilePos(string content, int line, int col)
{
if (line < 1)
return 0;
if (line == 1)
return (col > 0) ? col - 1 : 0;
int offset = -1;
while (line > 1) {
int tmp = content.IndexOf('\n', offset + 1);
if (tmp > -1) {
offset = tmp;
line--;
} else {
return content.Length;
}
}
return offset + col - 1;
}
public static int GetParentElementStart(ITextEditor editor)
{
Stack<int> offsetStack = new Stack<int>();
using (XmlTextReader xmlReader = new XmlTextReader(new StringReader(editor.Document.GetText(0, editor.Caret.Offset)))) {
try {
xmlReader.XmlResolver = null; // prevent XmlTextReader from loading external DTDs
while (xmlReader.Read()) {
switch (xmlReader.NodeType) {
case XmlNodeType.Element:
if (!xmlReader.IsEmptyElement) {
offsetStack.Push(editor.Document.PositionToOffset(xmlReader.LineNumber, xmlReader.LinePosition));
}
break;
case XmlNodeType.EndElement:
offsetStack.Pop();
break;
}
}
} catch (XmlException) { }
}
return (offsetStack.Count > 0) ? offsetStack.Pop() : -1;
}
public static XmlTextReader CreateReaderAtTarget(string fileContent, int caretLine, int caretColumn)
{
XmlTextReader r = new XmlTextReader(new StringReader(fileContent));
r.XmlResolver = null;
try {
r.WhitespaceHandling = WhitespaceHandling.Significant;
// move reader to correct position
while (r.Read() && !IsReaderAtTarget(r, caretLine, caretColumn)) { }
} catch (XmlException) {}
return r;
}
public static MarkupExtensionInfo GetInnermostMarkup(MarkupExtensionInfo markup)
{
var last = markup.PositionalArguments.LastOrDefault();
if (markup.NamedArguments.Count > 0)
last = markup.NamedArguments.LastOrDefault().Value;
if (last != null) {
if (!last.IsString) {
return GetInnermostMarkup(last.ExtensionValue);
}
}
return markup;
}
/// <summary>
/// Gets the of a markup extension at the given position.
/// </summary>
/// <param name="info">The markup extension data to parse.</param>
/// <param name="offset">The offset to look at.</param>
/// <returns>
/// A string, if the at offset is the extension type. <br />
/// An AttributeValue, if at the offset is a positional argument. <br />
/// A KeyValuePair&lt;string, AttributeValue&gt;, if at the offset is a named argument.
/// </returns>
public static object GetMarkupDataAtPosition(MarkupExtensionInfo info, int offset)
{
object previous = info.ExtensionType;
Debug.Print("offset: " + offset);
foreach (var item in info.PositionalArguments) {
if (item.StartOffset > offset)
break;
previous = item.IsString ? item : GetMarkupDataAtPosition(item.ExtensionValue, offset - item.StartOffset);
}
foreach (var pair in info.NamedArguments) {
if (pair.Value.StartOffset > offset)
break;
previous = pair.Value.IsString ? pair.Value : GetMarkupDataAtPosition(pair.Value.ExtensionValue, offset - pair.Value.StartOffset);
}
return previous;
}
} }
} }

4
src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.addin

@ -41,4 +41,8 @@
<Path name = "/AddIns/DefaultTextEditor/CodeCompletion"> <Path name = "/AddIns/DefaultTextEditor/CodeCompletion">
<CodeCompletionBinding id = "Xaml" extensions = ".xaml" class = "ICSharpCode.XamlBinding.XamlCodeCompletionBinding"/> <CodeCompletionBinding id = "Xaml" extensions = ".xaml" class = "ICSharpCode.XamlBinding.XamlCodeCompletionBinding"/>
</Path> </Path>
<Path name = "/AddIns/DefaultTextEditor/Formatter/XML">
<Class id ="XmlFormatter" class = "ICSharpCode.XmlEditor.XmlFormattingStrategy"/>
</Path>
</AddIn> </AddIn>

1
src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj

@ -80,7 +80,6 @@
<Compile Include="XamlContext.cs" /> <Compile Include="XamlContext.cs" />
<Compile Include="XamlExpressionContext.cs" /> <Compile Include="XamlExpressionContext.cs" />
<Compile Include="XamlExpressionFinder.cs"> <Compile Include="XamlExpressionFinder.cs">
<DependentUpon>XamlExpressionContext.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="XamlColorizer.cs" /> <Compile Include="XamlColorizer.cs" />
<Compile Include="MarkupExtensionParseException.cs" /> <Compile Include="MarkupExtensionParseException.cs" />

139
src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs

@ -5,13 +5,15 @@
// <version>$Revision: 3731 $</version> // <version>$Revision: 3731 $</version>
// </file> // </file>
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
using ICSharpCode.XmlEditor; using ICSharpCode.XmlEditor;
namespace ICSharpCode.XamlBinding namespace ICSharpCode.XamlBinding
@ -33,7 +35,7 @@ namespace ICSharpCode.XamlBinding
{ {
ParseInformation info = ParserService.GetParseInformation(editor.FileName); ParseInformation info = ParserService.GetParseInformation(editor.FileName);
XamlContext context = CompletionDataHelper.ResolveContext(editor, ch); XamlCompletionContext context = CompletionDataHelper.ResolveCompletionContext(editor, ch);
ICompletionItemList list; ICompletionItemList list;
if (context.Description == XamlContextDescription.InComment) if (context.Description == XamlContextDescription.InComment)
@ -41,13 +43,12 @@ namespace ICSharpCode.XamlBinding
switch (ch) { switch (ch) {
case '<': case '<':
context.Description = (context.Description == XamlContextDescription.None) ? XamlContextDescription.AtTag : context.Description;
list = CompletionDataHelper.CreateListForContext(editor, context); list = CompletionDataHelper.CreateListForContext(editor, context);
editor.ShowCompletionWindow(list); editor.ShowCompletionWindow(list);
return CodeCompletionKeyPressResult.Completed; return CodeCompletionKeyPressResult.Completed;
case '>': // XML tag completion case '>':
if (DoXmlTagCompletion(editor)) return CodeCompletionKeyPressResult.None;
return CodeCompletionKeyPressResult.EatKey;
break;
case '"': case '"':
if (!XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) { if (!XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) {
int search = editor.Caret.Offset + 1; int search = editor.Caret.Offset + 1;
@ -71,12 +72,13 @@ namespace ICSharpCode.XamlBinding
editor.Document.Insert(editor.Caret.Offset, "{}"); editor.Document.Insert(editor.Caret.Offset, "{}");
editor.Caret.Offset--; editor.Caret.Offset--;
DoMarkupExtensionCompletion(editor, info, CompletionDataHelper.ResolveContext(editor, '{')); DoMarkupExtensionCompletion(editor, info, CompletionDataHelper.ResolveCompletionContext(editor, '{'));
return CodeCompletionKeyPressResult.EatKey; return CodeCompletionKeyPressResult.EatKey;
} }
break; break;
case '.': case '.':
if (context.Path != null && !XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) { if (context.Path != null && !XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) {
context.Description = XamlContextDescription.AtTag;
list = CompletionDataHelper.CreateListForContext(editor, context); list = CompletionDataHelper.CreateListForContext(editor, context);
editor.ShowCompletionWindow(list); editor.ShowCompletionWindow(list);
return CodeCompletionKeyPressResult.Completed; return CodeCompletionKeyPressResult.Completed;
@ -84,20 +86,26 @@ namespace ICSharpCode.XamlBinding
break; break;
case ':': case ':':
if (context.Path != null && XmlParser.GetQualifiedAttributeNameAtIndex(editor.Document.Text, editor.Caret.Offset) == null) { if (context.Path != null && XmlParser.GetQualifiedAttributeNameAtIndex(editor.Document.Text, editor.Caret.Offset) == null) {
list = CompletionDataHelper.CreateListForContext(editor, context); if (!context.AttributeName.StartsWith("xmlns")) {
editor.ShowCompletionWindow(list); list = CompletionDataHelper.CreateListForContext(editor, context);
return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion; editor.ShowCompletionWindow(list);
return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion;
}
} }
break; break;
case '/': // ignore '/' when trying to type '/>' case '/': // ignore '/' when trying to type '/>'
return CodeCompletionKeyPressResult.None; return CodeCompletionKeyPressResult.None;
case '=': case '=':
if (!XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) { if (!XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) {
int searchOffset = editor.Caret.Offset + 1; int searchOffset = editor.Caret.Offset;
while (char.IsWhiteSpace(editor.Document.GetCharAt(searchOffset)))
while (searchOffset < editor.Document.TextLength - 1) {
searchOffset++; searchOffset++;
if (!char.IsWhiteSpace(editor.Document.GetCharAt(searchOffset)))
break;
}
if (editor.Document.GetCharAt(searchOffset) != '"') { if (searchOffset >= editor.Document.TextLength || editor.Document.GetCharAt(searchOffset) != '"') {
editor.Document.Insert(editor.Caret.Offset, "=\"\""); editor.Document.Insert(editor.Caret.Offset, "=\"\"");
editor.Caret.Offset--; editor.Caret.Offset--;
} else { } else {
@ -110,50 +118,28 @@ namespace ICSharpCode.XamlBinding
} }
break; break;
default: default:
editor.Document.Insert(editor.Caret.Offset, ch.ToString()); if (context.Description != XamlContextDescription.None && !char.IsWhiteSpace(context.PressedKey)) {
this.CtrlSpace(editor); editor.Document.Insert(editor.Caret.Offset, ch.ToString());
return CodeCompletionKeyPressResult.EatKey; if (!context.AttributeName.StartsWith("xmlns"))
this.CtrlSpace(editor);
return CodeCompletionKeyPressResult.EatKey;
}
break;
} }
return CodeCompletionKeyPressResult.None; return CodeCompletionKeyPressResult.None;
} }
static bool DoXmlTagCompletion(ITextEditor editor)
{
int offset = editor.Caret.Offset;
if (offset > 0) {
int searchOffset = offset - 1;
char c = editor.Document.GetCharAt(searchOffset);
while (char.IsWhiteSpace(c)) {
searchOffset--;
c = editor.Document.GetCharAt(searchOffset);
}
if (c != '/') {
string document = editor.Document.Text.Insert(offset, ">");
XmlElementPath path = XmlParser.GetActiveElementStartPathAtIndex(document, offset);
if (path != null && path.Elements.Count > 0) {
QualifiedName last = path.Elements[path.Elements.Count - 1];
if (!Utils.HasMatchingEndTag(last.Name, document, offset) && !last.Name.StartsWith("/") && !last.Name.StartsWith("!--")) {
editor.Document.Insert(offset, "></" + last.Name + ">");
editor.Caret.Offset = offset + 1;
return true;
}
}
}
}
return false;
}
public bool CtrlSpace(ITextEditor editor) public bool CtrlSpace(ITextEditor editor)
{ {
XamlContext context = CompletionDataHelper.ResolveContext(editor, ' '); XamlCompletionContext context = CompletionDataHelper.ResolveCompletionContext(editor, ' ');
context.Forced = true;
var info = ParserService.GetParseInformation(editor.FileName); var info = ParserService.GetParseInformation(editor.FileName);
if (context.Path != null) { if (context.Path != null) {
if (!XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) { if (!XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) {
var list = CompletionDataHelper.CreateListForContext(editor, context) as XamlCompletionItemList; var list = CompletionDataHelper.CreateListForContext(editor, context) as XamlCompletionItemList;
string starter = editor.GetWordBeforeCaret().Trim('<'); string starter = editor.GetWordBeforeCaret().Trim('<', '>');
if (!string.IsNullOrEmpty(starter) && !starter.EndsWith(StringComparison.Ordinal, ' ', '\t', '\n', '\r')) if (!string.IsNullOrEmpty(starter) && !starter.EndsWith(StringComparison.Ordinal, ' ', '\t', '\n', '\r'))
list.PreselectionLength = starter.Length; list.PreselectionLength = starter.Length;
editor.ShowCompletionWindow(list); editor.ShowCompletionWindow(list);
@ -167,23 +153,74 @@ namespace ICSharpCode.XamlBinding
if (!DoMarkupExtensionCompletion(editor, info, context)) { if (!DoMarkupExtensionCompletion(editor, info, context)) {
XamlResolver resolver = new XamlResolver(); XamlResolver resolver = new XamlResolver();
var mrr = resolver.Resolve(new ExpressionResult(context.AttributeName, new XamlExpressionContext(context.Path, context.AttributeName, false)), var completionList = new XamlCompletionItemList();
info, editor.Document.Text) as MemberResolveResult;
var mrr = resolver.Resolve(new ExpressionResult(context.AttributeName, context),
context.ParseInformation, context.Editor.Document.Text) as MemberResolveResult;
if (mrr != null && mrr.ResolvedType != null) { if (mrr != null && mrr.ResolvedType != null) {
var c = mrr.ResolvedType.GetUnderlyingClass(); var c = mrr.ResolvedType.GetUnderlyingClass();
var completionList = new XamlCompletionItemList();
completionList.Items.AddRange(CompletionDataHelper.MemberCompletion(editor, mrr.ResolvedType)); completionList.Items.AddRange(CompletionDataHelper.MemberCompletion(context, mrr.ResolvedType));
editor.ShowCompletionWindow(completionList);
editor.ShowInsightWindow(CompletionDataHelper.MemberInsight(mrr)); editor.ShowInsightWindow(CompletionDataHelper.MemberInsight(mrr));
} }
if (context.AttributeName == "Property" && context.Path.Elements.LastOrDefault().Name == "Setter") {
int offset = Utils.GetParentElementStart(editor);
var loc = editor.Document.OffsetToPosition(offset);
string[] attributes = Utils.GetListOfExistingAttributeNames(editor.Document.Text, loc.Line, loc.Column);
if (attributes.Contains("TargetType")) {
AttributeValue value = MarkupExtensionParser.ParseValue(Utils.GetAttributeValue(editor.Document.Text, loc.Line, loc.Column, "TargetType"));
if (!value.IsString) {
TypeResolveResult trr = CompletionDataHelper.ResolveMarkupExtensionType(value.ExtensionValue, context);
var typeName = CompletionDataHelper.ResolveType(GetTypeNameFromTypeExtension(value.ExtensionValue), context);
if (trr != null && trr.ResolvedClass != null && trr.ResolvedClass.FullyQualifiedName == "System.Windows.Markup.TypeExtension"
&& typeName != null && typeName != null) {
completionList.Items.AddRange(
typeName.ResolvedClass.Properties
.Where(p => p.IsPublic && p.CanSet)
.Select(prop => new DefaultCompletionItem(prop.Name))
.Cast<ICompletionItem>()
);
}
}
}
}
if (context.AttributeName.StartsWith("xmlns"))
completionList.Items.AddRange(CompletionDataHelper.CreateListForXmlnsCompletion(info.BestCompilationUnit.ProjectContent));
ICompletionListWindow window = editor.ShowCompletionWindow(completionList);
if (context.AttributeName.StartsWith("xmlns"))
window.Width = double.NaN;
return true;
} }
} }
} }
} }
return false; return false;
} }
static string GetTypeNameFromTypeExtension(MarkupExtensionInfo info)
{
var item = info.PositionalArguments.FirstOrDefault();
if (item != null && item.IsString) {
return item.StringValue;
} else {
if (info.NamedArguments.TryGetValue("typename", out item)) {
if (item.IsString)
return item.StringValue;
}
}
return string.Empty;
}
static bool DoMarkupExtensionCompletion(ITextEditor editor, ParseInformation info, XamlContext context) static bool DoMarkupExtensionCompletion(ITextEditor editor, ParseInformation info, XamlCompletionContext context)
{ {
if (context.AttributeValue != null && !context.AttributeValue.IsString) { if (context.AttributeValue != null && !context.AttributeValue.IsString) {
var completionList = CompletionDataHelper.CreateMarkupExtensionCompletion(context, info, editor); var completionList = CompletionDataHelper.CreateMarkupExtensionCompletion(context, info, editor);

16
src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizer.cs

@ -65,7 +65,7 @@ namespace ICSharpCode.XamlBinding
{ {
int index = -1; int index = -1;
List<HighlightingInfo> infos = new List<HighlightingInfo>(); List<HighlightingInfo> infos = new List<HighlightingInfo>();
/*
do { do {
index = line.Text.IndexOf('=', index + 1); index = line.Text.IndexOf('=', index + 1);
if (index > -1) { if (index > -1) {
@ -76,7 +76,7 @@ namespace ICSharpCode.XamlBinding
infos.Add(new HighlightingInfo(expr, startIndex, startIndex + expr.Length, line.Offset, path)); infos.Add(new HighlightingInfo(expr, startIndex, startIndex + expr.Length, line.Offset, path));
} }
} }
} while (index > -1); } while (index > -1);*/
return infos.ToArray(); return infos.ToArray();
} }
@ -105,15 +105,15 @@ namespace ICSharpCode.XamlBinding
int startOffset; int startOffset;
int endOffset; int endOffset;
int lineOffset; int lineOffset;
XmlElementPath path; XamlContext context;
public HighlightingInfo(string token, int startOffset, int endOffset, int lineOffset, XmlElementPath path) public HighlightingInfo(string token, int startOffset, int endOffset, int lineOffset, XamlContext context)
{ {
this.token = token; this.token = token;
this.startOffset = startOffset; this.startOffset = startOffset;
this.endOffset = endOffset; this.endOffset = endOffset;
this.lineOffset = lineOffset; this.lineOffset = lineOffset;
this.path = path; this.context = context;
} }
public string Token { public string Token {
@ -132,12 +132,12 @@ namespace ICSharpCode.XamlBinding
get { return lineOffset; } get { return lineOffset; }
} }
public XmlElementPath Path { public XamlContext Context {
get { return path; } get { return context; }
} }
public ExpressionResult GetExpressionResult() { public ExpressionResult GetExpressionResult() {
return new ExpressionResult(token, new XamlExpressionContext(path, token, false)); return new ExpressionResult(token, context);
} }
} }
} }

91
src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItem.cs

@ -14,24 +14,27 @@ using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.XamlBinding namespace ICSharpCode.XamlBinding
{ {
class XamlCompletionItem : CodeCompletionItem class XamlCodeCompletionItem : CodeCompletionItem
{ {
public XamlCompletionItem(IEntity entity, string prefix) public XamlCodeCompletionItem(IEntity entity, string prefix, bool addOpeningBrace)
: base(entity) : base(entity)
{ {
if (string.IsNullOrEmpty(prefix)) if (string.IsNullOrEmpty(prefix))
this.Text = entity.Name; this.Text = entity.Name;
else else
this.Text = prefix + ":" + entity.Name; this.Text = prefix + ":" + entity.Name;
if (addOpeningBrace)
this.Text = "<" + this.Text;
} }
public XamlCompletionItem(IEntity entity) public XamlCodeCompletionItem(IEntity entity)
: base(entity) : base(entity)
{ {
this.Text = entity.Name; this.Text = entity.Name;
} }
public XamlCompletionItem(string text, IEntity entity) public XamlCodeCompletionItem(IEntity entity, string text)
: base(entity) : base(entity)
{ {
this.Text = text; this.Text = text;
@ -43,6 +46,86 @@ namespace ICSharpCode.XamlBinding
} }
} }
class XamlCompletionItem : DefaultCompletionItem
{
string prefix, @namespace, name;
public XamlCompletionItem(string prefix, string @namespace, string name)
: base(prefix + ":" + name)
{
this.prefix = prefix;
this.@namespace = @namespace;
this.name = name;
}
public XamlCompletionItem(string @namespace, string name)
: base(name)
{
this.prefix = "";
this.@namespace = @namespace;
this.name = name;
}
public string Prefix {
get { return prefix; }
}
public string Namespace {
get { return @namespace; }
}
public string Name {
get { return name; }
}
}
class XmlnsCompletionItem : DefaultCompletionItem
{
string @namespace, assembly;
bool isUrl;
public bool IsUrl {
get { return isUrl; }
}
public XmlnsCompletionItem(string @namespace, string assembly)
: base(@namespace + " (" + assembly + ")")
{
this.@namespace = @namespace;
this.assembly = assembly;
}
public XmlnsCompletionItem(string @namespace, bool isUrl)
: base(@namespace)
{
this.@namespace = @namespace;
this.isUrl = isUrl;
this.assembly = string.Empty;
}
public string Namespace {
get { return @namespace; }
}
public string Assembly {
get { return assembly; }
}
public override void Complete(CompletionContext context)
{
if (isUrl)
base.Complete(context);
else {
ITextEditor editor = context.Editor;
string newText = "clr-namespace:" + @namespace;
if (!string.IsNullOrEmpty(assembly))
newText += ";assembly=" + assembly;
editor.Document.Replace(context.StartOffset, context.Length, newText);
context.EndOffset = context.StartOffset + newText.Length;
}
}
}
class NewEventCompletionItem : DefaultCompletionItem class NewEventCompletionItem : DefaultCompletionItem
{ {
IEvent eventType; IEvent eventType;

39
src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs

@ -26,7 +26,7 @@ namespace ICSharpCode.XamlBinding
public override CompletionItemListKeyResult ProcessInput(char key) public override CompletionItemListKeyResult ProcessInput(char key)
{ {
if (key == ':') if (key == ':' || key == '/')
return CompletionItemListKeyResult.NormalKey; return CompletionItemListKeyResult.NormalKey;
return base.ProcessInput(key); return base.ProcessInput(key);
@ -36,8 +36,8 @@ namespace ICSharpCode.XamlBinding
{ {
base.Complete(context, item); base.Complete(context, item);
if (item is XamlCompletionItem) { if (item is XamlCodeCompletionItem) {
XamlCompletionItem cItem = item as XamlCompletionItem; XamlCodeCompletionItem cItem = item as XamlCodeCompletionItem;
if (cItem.Entity is IProperty || cItem.Entity is IEvent) { if (cItem.Entity is IProperty || cItem.Entity is IEvent) {
if (context.Editor.Document.GetCharAt(context.StartOffset - 1) != '.') { if (context.Editor.Document.GetCharAt(context.StartOffset - 1) != '.') {
@ -45,17 +45,22 @@ namespace ICSharpCode.XamlBinding
context.Editor.Document.Insert(context.EndOffset, "=\"\""); context.Editor.Document.Insert(context.EndOffset, "=\"\"");
context.Editor.Caret.Offset--; context.Editor.Caret.Offset--;
} else { } else {
XamlContext xamlContext = CompletionDataHelper.ResolveContext(context.Editor, context.CompletionChar); XamlCompletionContext xamlContext = CompletionDataHelper.ResolveCompletionContext(context.Editor, context.CompletionChar);
if (!string.IsNullOrEmpty(xamlContext.RawAttributeValue)) { if (!string.IsNullOrEmpty(xamlContext.RawAttributeValue)) {
string valuePart = xamlContext.RawAttributeValue.Substring(0, xamlContext.ValueStartOffset); string valuePart = xamlContext.RawAttributeValue.Substring(0, xamlContext.ValueStartOffset);
AttributeValue value = MarkupExtensionParser.ParseValue(valuePart); AttributeValue value = MarkupExtensionParser.ParseValue(valuePart);
if (value != null && !value.IsString) { if (value != null && !value.IsString) {
var markup = CompletionDataHelper.GetInnermostMarkup(value.ExtensionValue); var markup = Utils.GetInnermostMarkup(value.ExtensionValue);
if (markup.NamedArguments.Count > 0 || markup.PositionalArguments.Count > 0) { if (markup.NamedArguments.Count > 0 || markup.PositionalArguments.Count > 0) {
int oldOffset = context.Editor.Caret.Offset; int oldOffset = context.Editor.Caret.Offset;
context.Editor.Caret.Offset = context.StartOffset; context.Editor.Caret.Offset = context.StartOffset;
string word = context.Editor.GetWordBeforeCaret(); string word = context.Editor.GetWordBeforeCaret().Trim();
if (!word.EndsWith(",") && markup.ExtensionType != word) {
context.Editor.Document.Insert(context.Editor.Caret.Offset, ", ");
oldOffset += 2;
}
context.Editor.Caret.Offset = oldOffset; context.Editor.Caret.Offset = oldOffset;
} }
@ -75,7 +80,7 @@ namespace ICSharpCode.XamlBinding
insertionString = " "; insertionString = " ";
} }
string prefix = Utils.GetXamlNamespacePrefix(context.Editor.Document.Text, context.StartOffset); string prefix = Utils.GetXamlNamespacePrefix(CompletionDataHelper.ResolveCompletionContext(context.Editor, context.CompletionChar));
if (!string.IsNullOrEmpty(prefix)) if (!string.IsNullOrEmpty(prefix))
prefix += ":"; prefix += ":";
@ -96,11 +101,21 @@ namespace ICSharpCode.XamlBinding
XamlCodeCompletionBinding.Instance.CtrlSpace(context.Editor); XamlCodeCompletionBinding.Instance.CtrlSpace(context.Editor);
} }
} }
} else { }
if (item is NewEventCompletionItem) {
NewEventCompletionItem eventItem = item as NewEventCompletionItem; if (item is NewEventCompletionItem) {
CreateEventHandlerCode(context, eventItem); NewEventCompletionItem eventItem = item as NewEventCompletionItem;
} CreateEventHandlerCode(context, eventItem);
}
if (item is XmlnsCompletionItem) {
context.Editor.Caret.Offset++;
}
if (item is XamlCompletionItem) {
XamlCompletionItem xamlItem = item as XamlCompletionItem;
context.Editor.Document.Insert(context.EndOffset, "=\"\"");
context.Editor.Caret.Offset--;
} }
} }

24
src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs

@ -5,12 +5,12 @@
// <version>$Revision: 3731 $</version> // <version>$Revision: 3731 $</version>
// </file> // </file>
using ICSharpCode.SharpDevelop.Editor;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Xml; using System.Xml;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor; using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
@ -18,18 +18,23 @@ using ICSharpCode.XmlEditor;
namespace ICSharpCode.XamlBinding namespace ICSharpCode.XamlBinding
{ {
public class XamlContext { public class XamlContext : ExpressionContext {
public XmlElementPath Path { get; set; } public XmlElementPath Path { get; set; }
public string AttributeName { get; set; } public string AttributeName { get; set; }
public ResolveResult ResolvedExpression { get; set; }
public AttributeValue AttributeValue { get; set; } public AttributeValue AttributeValue { get; set; }
public char PressedKey { get; set; }
public string RawAttributeValue { get; set; } public string RawAttributeValue { get; set; }
public int ValueStartOffset { get; set; } public int ValueStartOffset { get; set; }
public XamlContextDescription Description { get; set; } public XamlContextDescription Description { get; set; }
public Dictionary<string, string> XmlnsDefinitions { get; set; }
public ParseInformation ParseInformation { get; set; }
public XamlContext() {} public XamlContext() {}
public override bool ShowEntry(object o)
{
return true;
}
public override string ToString() public override string ToString()
{ {
return "[XamlContext" + return "[XamlContext" +
@ -39,10 +44,19 @@ namespace ICSharpCode.XamlBinding
" ValueStartOffset: " + ValueStartOffset + " ValueStartOffset: " + ValueStartOffset +
" ]"; " ]";
} }
}
public class XamlCompletionContext : XamlContext {
public char PressedKey { get; set; }
public bool Forced { get; set; }
public ITextEditor Editor { get; set; }
} }
public enum XamlContextDescription { public enum XamlContextDescription {
/// <summary>
/// Outside any tag
/// </summary>
None,
/// <summary> /// <summary>
/// After '&lt;' /// After '&lt;'
/// </summary> /// </summary>

114
src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs

@ -23,59 +23,70 @@ namespace ICSharpCode.XamlBinding
public class XamlResolver : IResolver public class XamlResolver : IResolver
{ {
IClass callingClass; IClass callingClass;
string fileContent;
string resolveExpression; string resolveExpression;
XamlExpressionContext context; int caretLine, caretColumn;
ParseInformation parseInfo; XamlContext context;
int caretLineNumber, caretColumn;
internal bool IsReaderAtTarget(XmlTextReader r)
{
if (r.LineNumber > caretLineNumber)
return true;
else if (r.LineNumber == caretLineNumber)
return r.LinePosition >= caretColumn;
else
return false;
}
public ResolveResult Resolve(ExpressionResult expressionResult, ParseInformation parseInfo, string fileContent) public ResolveResult Resolve(ExpressionResult expressionResult, ParseInformation parseInfo, string fileContent)
{ {
this.resolveExpression = expressionResult.Expression; this.resolveExpression = expressionResult.Expression;
this.parseInfo = parseInfo; this.caretLine = expressionResult.Region.BeginLine;
this.caretLineNumber = expressionResult.Region.BeginLine;
this.caretColumn = expressionResult.Region.BeginColumn; this.caretColumn = expressionResult.Region.BeginColumn;
this.callingClass = parseInfo.BestCompilationUnit.GetInnermostClass(caretLineNumber, caretColumn); this.fileContent = fileContent;
this.context = expressionResult.Context as XamlExpressionContext; this.callingClass = parseInfo.BestCompilationUnit.GetInnermostClass(caretLine, caretColumn);
if (context == null) this.context = expressionResult.Context as XamlContext ?? CompletionDataHelper.ResolveContext(fileContent, parseInfo.MostRecentCompilationUnit.FileName, caretLine, caretColumn);
return null;
try { switch (this.context.Description) {
using (XmlTextReader r = new XmlTextReader(new StringReader(fileContent))) { case XamlContextDescription.AtTag:
r.WhitespaceHandling = WhitespaceHandling.Significant; return ResolveElementName(resolveExpression);
// move reader to correct position case XamlContextDescription.InTag:
while (r.Read() && !IsReaderAtTarget(r)) { } return ResolveAttribute(resolveExpression);
case XamlContextDescription.InAttributeValue:
if (string.IsNullOrEmpty(context.AttributeName)) { MemberResolveResult mrr = ResolveAttribute(context.AttributeName);
return ResolveElementName(r, expressionResult.Expression); if (mrr != null) {
} return ResolveAttributeValue(mrr.ResolvedMember, resolveExpression) ?? mrr;
else if (context.InAttributeValue) {
MemberResolveResult mrr = ResolveAttribute(r, context.AttributeName);
if (mrr != null) {
return ResolveAttributeValue(mrr.ResolvedMember, resolveExpression);
}
} }
else { break;
// in attribute name case XamlContextDescription.InMarkupExtension:
return ResolveAttribute(r, resolveExpression); return ResolveMarkupExtension(resolveExpression);
}
return null;
}
ResolveResult ResolveMarkupExtension(string expression)
{
if (context.AttributeValue.IsString)
return null;
object data = Utils.GetMarkupDataAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset);
// resolve markup extension type
if ((data as string) == expression) {
return ResolveElementName(expression + "Extension") ?? ResolveElementName(expression);
} else {
var value = data as AttributeValue;
if (value != null && value.IsString) {
return ResolveElementName(expression) ?? ResolveAttribute(expression);
}
if (data is KeyValuePair<string, AttributeValue>) {
var pair = (KeyValuePair<string, AttributeValue>)data;
var member = ResolveAttribute(pair.Key);
if (pair.Value.StartOffset + pair.Key.Length >= context.ValueStartOffset) {
return member;
} else {
if (pair.Value.IsString && member != null)
return ResolveAttributeValue(member.ResolvedMember, expression);
} }
} }
return null;
}
catch (XmlException) {
return null; return null;
} }
} }
ResolveResult ResolveElementName(XmlReader r, string exp) ResolveResult ResolveElementName(string exp)
{ {
string xmlNamespace; string xmlNamespace;
string name; string name;
@ -83,10 +94,12 @@ namespace ICSharpCode.XamlBinding
if (resolveExpression.Contains(":")) { if (resolveExpression.Contains(":")) {
string prefix = resolveExpression.Substring(0, resolveExpression.IndexOf(':')); string prefix = resolveExpression.Substring(0, resolveExpression.IndexOf(':'));
name = resolveExpression.Substring(resolveExpression.IndexOf(':') + 1); name = resolveExpression.Substring(resolveExpression.IndexOf(':') + 1);
xmlNamespace = r.LookupNamespace(prefix); if (!context.XmlnsDefinitions.TryGetValue(prefix, out xmlNamespace))
xmlNamespace = null;
} }
else { else {
xmlNamespace = r.LookupNamespace(""); if (!context.XmlnsDefinitions.TryGetValue("", out xmlNamespace))
xmlNamespace = null;
name = resolveExpression; name = resolveExpression;
} }
if (name.Contains(".")) { if (name.Contains(".")) {
@ -95,7 +108,7 @@ namespace ICSharpCode.XamlBinding
return ResolveProperty(xmlNamespace, name, propertyName, true); return ResolveProperty(xmlNamespace, name, propertyName, true);
} }
else { else {
IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent; IProjectContent pc = context.ParseInformation.BestCompilationUnit.ProjectContent;
IReturnType resolvedType = XamlCompilationUnit.FindType(pc, xmlNamespace, name); IReturnType resolvedType = XamlCompilationUnit.FindType(pc, xmlNamespace, name);
IClass resolvedClass = resolvedType != null ? resolvedType.GetUnderlyingClass() : null; IClass resolvedClass = resolvedType != null ? resolvedType.GetUnderlyingClass() : null;
if (resolvedClass != null) { if (resolvedClass != null) {
@ -109,7 +122,7 @@ namespace ICSharpCode.XamlBinding
MemberResolveResult ResolveProperty(string xmlNamespace, string className, string propertyName, bool allowAttached) MemberResolveResult ResolveProperty(string xmlNamespace, string className, string propertyName, bool allowAttached)
{ {
IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent; IProjectContent pc = context.ParseInformation.BestCompilationUnit.ProjectContent;
IReturnType resolvedType = XamlCompilationUnit.FindType(pc, xmlNamespace, className); IReturnType resolvedType = XamlCompilationUnit.FindType(pc, xmlNamespace, className);
if (resolvedType != null && resolvedType.GetUnderlyingClass() != null) { if (resolvedType != null && resolvedType.GetUnderlyingClass() != null) {
IMember member = resolvedType.GetProperties().Find(delegate(IProperty p) { return p.Name == propertyName; }); IMember member = resolvedType.GetProperties().Find(delegate(IProperty p) { return p.Name == propertyName; });
@ -140,18 +153,21 @@ namespace ICSharpCode.XamlBinding
return null; return null;
} }
MemberResolveResult ResolveAttribute(XmlReader r, string attributeName) MemberResolveResult ResolveAttribute(string attributeName)
{ {
if (context.ElementPath.Elements.Count == 0) { if (context.Path == null) {
return null; return null;
} }
string attributeXmlNamespace; string attributeXmlNamespace;
if (attributeName.Contains(":")) { if (attributeName.Contains(":")) {
attributeXmlNamespace = r.LookupNamespace(attributeName.Substring(0, attributeName.IndexOf(':'))); string prefix = attributeName.Substring(0, attributeName.IndexOf(':'));
if (!context.XmlnsDefinitions.TryGetValue(prefix, out attributeXmlNamespace))
attributeXmlNamespace = null;
attributeName = attributeName.Substring(attributeName.IndexOf(':') + 1); attributeName = attributeName.Substring(attributeName.IndexOf(':') + 1);
} }
else { else {
attributeXmlNamespace = r.LookupNamespace(""); if (!context.XmlnsDefinitions.TryGetValue("", out attributeXmlNamespace))
attributeXmlNamespace = null;
} }
if (attributeName.Contains(".")) { if (attributeName.Contains(".")) {
string className = attributeName.Substring(0, attributeName.IndexOf('.')); string className = attributeName.Substring(0, attributeName.IndexOf('.'));
@ -159,7 +175,7 @@ namespace ICSharpCode.XamlBinding
return ResolveProperty(attributeXmlNamespace, className, attributeName, true); return ResolveProperty(attributeXmlNamespace, className, attributeName, true);
} }
else { else {
ICSharpCode.XmlEditor.QualifiedName lastElement = context.ElementPath.Elements[context.ElementPath.Elements.Count - 1]; ICSharpCode.XmlEditor.QualifiedName lastElement = context.Path.Elements.LastOrDefault();
return ResolveProperty(lastElement.Namespace, lastElement.Name, attributeName, false); return ResolveProperty(lastElement.Namespace, lastElement.Name, attributeName, false);
} }
} }

36
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -391,7 +391,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
void OnCodeCompletion(object sender, ExecutedRoutedEventArgs e) void OnCodeCompletion(object sender, ExecutedRoutedEventArgs e)
{ {
CloseExistingCompletionWindows(); CloseExistingCompletionWindow();
TextEditor textEditor = GetTextEditorFromSender(sender); TextEditor textEditor = GetTextEditorFromSender(sender);
foreach (ICodeCompletionBinding cc in CodeCompletionBindings) { foreach (ICodeCompletionBinding cc in CodeCompletionBindings) {
if (cc.CtrlSpace(GetAdapter(textEditor))) { if (cc.CtrlSpace(GetAdapter(textEditor))) {
@ -401,39 +401,61 @@ namespace ICSharpCode.AvalonEdit.AddIn
} }
} }
CompletionWindow completionWindow; SharpDevelopCompletionWindow completionWindow;
SharpDevelopInsightWindow insightWindow; SharpDevelopInsightWindow insightWindow;
void CloseExistingCompletionWindows() void CloseExistingCompletionWindow()
{ {
if (completionWindow != null) { if (completionWindow != null) {
completionWindow.Close(); completionWindow.Close();
} }
}
void CloseExistingInsightWindow()
{
if (insightWindow != null) { if (insightWindow != null) {
insightWindow.Close(); insightWindow.Close();
} }
} }
public SharpDevelopCompletionWindow ActiveCompletionWindow {
get { return completionWindow; }
}
public SharpDevelopInsightWindow ActiveInsightWindow { public SharpDevelopInsightWindow ActiveInsightWindow {
get { return insightWindow; } get { return insightWindow; }
} }
internal void NotifyCompletionWindowOpened(CompletionWindow window) internal void ShowCompletionWindow(SharpDevelopCompletionWindow window)
{ {
CloseExistingCompletionWindows(); CloseExistingCompletionWindow();
completionWindow = window; completionWindow = window;
window.Closed += delegate { window.Closed += delegate {
completionWindow = null; completionWindow = null;
}; };
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(
delegate {
if (completionWindow == window) {
window.Show();
}
}
));
} }
internal void NotifyInsightWindowOpened(SharpDevelopInsightWindow window) internal void ShowInsightWindow(SharpDevelopInsightWindow window)
{ {
CloseExistingCompletionWindows(); CloseExistingInsightWindow();
insightWindow = window; insightWindow = window;
window.Closed += delegate { window.Closed += delegate {
insightWindow = null; insightWindow = null;
}; };
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(
delegate {
if (insightWindow == window) {
window.Show();
}
}
));
} }
IFormattingStrategy formattingStrategy; IFormattingStrategy formattingStrategy;

18
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorAdapter.cs

@ -37,13 +37,17 @@ namespace ICSharpCode.AvalonEdit.AddIn
get { return codeEditor.FormattingStrategy; } get { return codeEditor.FormattingStrategy; }
} }
public override void ShowCompletionWindow(ICompletionItemList data) public override ICompletionListWindow ActiveCompletionWindow {
get { return codeEditor.ActiveCompletionWindow; }
}
public override ICompletionListWindow ShowCompletionWindow(ICompletionItemList data)
{ {
if (data == null || !data.Items.Any()) if (data == null || !data.Items.Any())
return; return null;
CompletionWindow window = new SharpDevelopCompletionWindow(this, this.TextEditor.TextArea, data); SharpDevelopCompletionWindow window = new SharpDevelopCompletionWindow(this, this.TextEditor.TextArea, data);
codeEditor.NotifyCompletionWindowOpened(window); codeEditor.ShowCompletionWindow(window);
window.Show(); return window;
} }
public override IInsightWindow ShowInsightWindow(IEnumerable<IInsightItem> items) public override IInsightWindow ShowInsightWindow(IEnumerable<IInsightItem> items)
@ -58,9 +62,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
// don't open insight window when there are no items // don't open insight window when there are no items
return null; return null;
} }
codeEditor.NotifyInsightWindowOpened(insightWindow); codeEditor.ShowInsightWindow(insightWindow);
insightWindow.Show();
return insightWindow; return insightWindow;
} }

47
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/SharpDevelopCompletionWindow.cs

@ -5,23 +5,60 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
using System; using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using ICSharpCode.AvalonEdit.CodeCompletion; using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
namespace ICSharpCode.AvalonEdit.AddIn namespace ICSharpCode.AvalonEdit.AddIn
{ {
/// <summary> /// <summary>
/// The code completion window. /// The code completion window.
/// </summary> /// </summary>
public class SharpDevelopCompletionWindow : CompletionWindow public class SharpDevelopCompletionWindow : CompletionWindow, ICompletionListWindow
{ {
public ICompletionItem SelectedItem {
get {
return ((CodeCompletionDataAdapter)this.CompletionList.SelectedItem).Item;
}
set {
var itemAdapters = this.CompletionList.CompletionData.Cast<CodeCompletionDataAdapter>();
this.CompletionList.SelectedItem = itemAdapters.FirstOrDefault(a => a.Item == value);
}
}
double ICompletionWindow.Width {
get { return this.Width; }
set {
// Disable virtualization if we use automatic width - this prevents the window from resizing
// when the user scrolls.
VirtualizingStackPanel.SetIsVirtualizing(this.CompletionList.ListBox, !double.IsNaN(value));
this.Width = value;
if (double.IsNaN(value)) {
// enable size-to-width:
if (this.SizeToContent == SizeToContent.Manual)
this.SizeToContent = SizeToContent.Width;
else if (this.SizeToContent == SizeToContent.Height)
this.SizeToContent = SizeToContent.WidthAndHeight;
} else {
// disable size-to-width:
if (this.SizeToContent == SizeToContent.Width)
this.SizeToContent = SizeToContent.Manual;
else if (this.SizeToContent == SizeToContent.WidthAndHeight)
this.SizeToContent = SizeToContent.Height;
}
}
}
readonly ICompletionItemList itemList; readonly ICompletionItemList itemList;
public ICompletionItemList ItemList { public ICompletionItemList ItemList {
@ -92,6 +129,10 @@ namespace ICSharpCode.AvalonEdit.AddIn
this.item = item; this.item = item;
} }
public ICompletionItem Item {
get { return item; }
}
public string Text { public string Text {
get { return item.Text; } get { return item.Text; }
} }
@ -107,7 +148,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
} }
public ImageSource Image { public ImageSource Image {
get { get {
IImage image = item.Image; IImage image = item.Image;
return image != null ? image.ImageSource : null; return image != null ? image.ImageSource : null;
} }

10
src/AddIns/DisplayBindings/XmlEditor/Project/Src/FormatXmlCommand.cs

@ -5,9 +5,10 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Editor;
using System; using System;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.XmlEditor namespace ICSharpCode.XmlEditor
{ {
@ -18,11 +19,10 @@ namespace ICSharpCode.XmlEditor
{ {
public override void Run() public override void Run()
{ {
// Find active XmlView. ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
XmlView xmlView = XmlView.ActiveXmlView;
if (xmlView != null) { if (provider != null) {
xmlView.FormatXml(); XmlView.FormatXml(provider.TextEditor);
} }
} }
} }

66
src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCodeCompletionBinding.cs

@ -1,29 +1,28 @@
// <file> // <file>
// <copyright see="prj:///doc/copyright.txt"/> // <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/> // <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="sie_pam@gmx.at"/> // <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using System; using System;
using System.IO; using System.IO;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
namespace ICSharpCode.XmlEditor namespace ICSharpCode.XmlEditor
{ {
/// <summary>
/// Description of XmlCodeCompletionBinding.
/// </summary>
public class XmlCodeCompletionBinding : ICodeCompletionBinding public class XmlCodeCompletionBinding : ICodeCompletionBinding
{ {
static XmlCodeCompletionBinding instance; static XmlCodeCompletionBinding instance;
public static XmlCodeCompletionBinding Instance { public static XmlCodeCompletionBinding Instance {
get { get {
if (instance == null) if (instance == null) {
instance = new XmlCodeCompletionBinding(); instance = new XmlCodeCompletionBinding();
}
return instance; return instance;
} }
} }
@ -33,45 +32,52 @@ namespace ICSharpCode.XmlEditor
} }
public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch)
{ {
string text = string.Concat(editor.Document.GetText(0, editor.Caret.Offset), ch); string text = editor.Document.GetText(0, editor.Caret.Offset);
string extension = Path.GetExtension(editor.FileName); XmlCompletionDataProvider provider = GetProvider(editor.FileName);
string defaultNamespacePrefix = XmlSchemaManager.GetNamespacePrefix(extension); ICompletionItemList completionItems = provider.GenerateCompletionData(text, ch);
XmlSchemaCompletionData defaultSchemaCompletionData = XmlSchemaManager.GetSchemaCompletionData(extension); ICompletionListWindow completionWindow = editor.ShowCompletionWindow(completionItems);
XmlCompletionDataProvider provider = new XmlCompletionDataProvider(XmlSchemaManager.SchemaCompletionDataItems, if (completionWindow != null) {
defaultSchemaCompletionData, XmlCompletionItem item = completionItems.SuggestedItem as XmlCompletionItem;
defaultNamespacePrefix); if (item.DataType == XmlCompletionDataType.NamespaceUri) {
completionWindow.Width = double.NaN;
editor.ShowCompletionWindow(provider.GenerateCompletionData(text, ch)); }
}
if (ch == '<' || ch == ' ' || ch == '=') if ((ch == '<') || (ch == ' ') || (ch == '=')) {
return CodeCompletionKeyPressResult.Completed; return CodeCompletionKeyPressResult.Completed;
else }
return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion; return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion;
} }
public bool CtrlSpace(ITextEditor editor) public bool CtrlSpace(ITextEditor editor)
{ {
// Attribute value completion.
string text = editor.Document.Text; string text = editor.Document.Text;
int offset = editor.Caret.Offset; int offset = editor.Caret.Offset;
string extension = Path.GetExtension(editor.FileName);
string defaultNamespacePrefix = XmlSchemaManager.GetNamespacePrefix(extension);
XmlSchemaCompletionData defaultSchemaCompletionData = XmlSchemaManager.GetSchemaCompletionData(extension);
XmlCompletionDataProvider provider = new XmlCompletionDataProvider(XmlSchemaManager.SchemaCompletionDataItems,
defaultSchemaCompletionData,
defaultNamespacePrefix);
// Attribute value completion.
if (XmlParser.IsInsideAttributeValue(text, offset)) { if (XmlParser.IsInsideAttributeValue(text, offset)) {
XmlElementPath path = XmlParser.GetActiveElementStartPath(text, offset); XmlElementPath path = XmlParser.GetActiveElementStartPath(text, offset);
if (path.Elements.Count > 0) { if (path.Elements.Count > 0) {
XmlCompletionDataProvider provider = GetProvider(editor.FileName);
editor.ShowCompletionWindow(provider.GetAttributeValueCompletionData(path, XmlParser.GetAttributeNameAtIndex(text, offset))); editor.ShowCompletionWindow(provider.GetAttributeValueCompletionData(path, XmlParser.GetAttributeNameAtIndex(text, offset)));
return true; return true;
} }
} }
return false; return false;
} }
public static XmlCompletionDataProvider GetProvider(string fileName)
{
string extension = Path.GetExtension(fileName);
if (PropertyService.DataDirectory != null) {
XmlSchemaCompletionDataCollection schemas = XmlSchemaManager.SchemaCompletionDataItems;
string defaultNamespacePrefix = XmlSchemaManager.GetNamespacePrefix(extension);
XmlSchemaCompletionData defaultSchemaCompletionData = XmlSchemaManager.GetSchemaCompletionData(extension);
return new XmlCompletionDataProvider(schemas, defaultSchemaCompletionData, defaultNamespacePrefix);
}
// for unit tests
return new XmlCompletionDataProvider(new XmlSchemaCompletionDataCollection(), null, String.Empty);
}
} }
} }

4
src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionDataProvider.cs

@ -141,7 +141,7 @@ namespace ICSharpCode.XmlEditor
public DefaultCompletionItemList GetAttributeCompletionData(XmlElementPath path) public DefaultCompletionItemList GetAttributeCompletionData(XmlElementPath path)
{ {
var list = new XmlCompletionItemList(); XmlCompletionItemList list = new XmlCompletionItemList();
XmlSchemaCompletionData schema = FindSchema(path); XmlSchemaCompletionData schema = FindSchema(path);
if (schema != null) { if (schema != null) {
@ -155,7 +155,7 @@ namespace ICSharpCode.XmlEditor
public DefaultCompletionItemList GetAttributeValueCompletionData(XmlElementPath path, string name) public DefaultCompletionItemList GetAttributeValueCompletionData(XmlElementPath path, string name)
{ {
var list = new XmlCompletionItemList(); XmlCompletionItemList list = new XmlCompletionItemList();
XmlSchemaCompletionData schema = FindSchema(path); XmlSchemaCompletionData schema = FindSchema(path);
if (schema != null) { if (schema != null) {

17
src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItem.cs

@ -28,10 +28,10 @@ namespace ICSharpCode.XmlEditor
public class XmlCompletionItem : DefaultCompletionItem public class XmlCompletionItem : DefaultCompletionItem
{ {
XmlCompletionDataType dataType = XmlCompletionDataType.XmlElement; XmlCompletionDataType dataType = XmlCompletionDataType.XmlElement;
string description = string.Empty; string description = String.Empty;
public XmlCompletionItem(string text) public XmlCompletionItem(string text)
: this(text, string.Empty, XmlCompletionDataType.XmlElement) : this(text, String.Empty, XmlCompletionDataType.XmlElement)
{ {
} }
@ -41,7 +41,7 @@ namespace ICSharpCode.XmlEditor
} }
public XmlCompletionItem(string text, XmlCompletionDataType dataType) public XmlCompletionItem(string text, XmlCompletionDataType dataType)
: this(text, string.Empty, dataType) : this(text, String.Empty, dataType)
{ {
} }
@ -57,9 +57,11 @@ namespace ICSharpCode.XmlEditor
/// the xs:annotation/xs:documentation element. /// the xs:annotation/xs:documentation element.
/// </summary> /// </summary>
public override string Description { public override string Description {
get { get { return description; }
return description; }
}
public XmlCompletionDataType DataType {
get { return dataType; }
} }
public override void Complete(CompletionContext context) public override void Complete(CompletionContext context)
@ -69,7 +71,7 @@ namespace ICSharpCode.XmlEditor
switch (dataType) { switch (dataType) {
case XmlCompletionDataType.NamespaceUri: case XmlCompletionDataType.NamespaceUri:
context.Editor.Document.Insert(context.StartOffset, "\""); context.Editor.Document.Insert(context.StartOffset, "\"");
context.Editor.Document.Insert(context.EndOffset, "\""); context.Editor.Document.Insert(context.EndOffset + 1, "\"");
break; break;
case XmlCompletionDataType.XmlAttribute: case XmlCompletionDataType.XmlAttribute:
context.Editor.Document.Insert(context.EndOffset, "=\"\""); context.Editor.Document.Insert(context.EndOffset, "=\"\"");
@ -83,6 +85,5 @@ namespace ICSharpCode.XmlEditor
{ {
return "[" + this.Text + "]"; return "[" + this.Text + "]";
} }
} }
} }

37
src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlFormattingStrategy.cs

@ -5,14 +5,16 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using ICSharpCode.SharpDevelop.Editor;
using System; using System;
using System.Collections; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.XmlEditor namespace ICSharpCode.XmlEditor
{ {
@ -52,7 +54,9 @@ namespace ICSharpCode.XmlEditor
} }
string tagString = tag.ToString(); string tagString = tag.ToString();
if (tagString.Length > 0 && !tagString.StartsWith("!", StringComparison.Ordinal) && !tagString.StartsWith("?", StringComparison.Ordinal)) { if (tagString.Length > 0 && !tagString.StartsWith("!", StringComparison.Ordinal) && !tagString.StartsWith("?", StringComparison.Ordinal)) {
int caretOffset = editor.Caret.Offset;
editor.Document.Insert(editor.Caret.Offset, "</" + tagString + ">"); editor.Document.Insert(editor.Caret.Offset, "</" + tagString + ">");
editor.Caret.Offset = caretOffset;
} }
} }
} }
@ -66,7 +70,7 @@ namespace ICSharpCode.XmlEditor
Debug.Assert(false, e.ToString()); Debug.Assert(false, e.ToString());
} }
if (charTyped == '\n') { if (charTyped == '\n') {
IndentLine(editor, editor.Document.GetLineForOffset(editor.Caret.Offset)); IndentLine(editor, editor.Document.GetLine(editor.Caret.Line));
} }
editor.Document.EndUndoableAction(); editor.Document.EndUndoableAction();
} }
@ -97,11 +101,11 @@ namespace ICSharpCode.XmlEditor
editor.Document.EndUndoableAction(); editor.Document.EndUndoableAction();
} }
} }
#region Smart Indentation
static void TryIndent(ITextEditor editor, int begin, int end) static void TryIndent(ITextEditor editor, int begin, int end)
{ {
string currentIndentation = ""; string currentIndentation = "";
Stack tagStack = new Stack(); Stack<string> tagStack = new Stack<string>();
IDocument document = editor.Document; IDocument document = editor.Document;
string tab = editor.Options.IndentationString; string tab = editor.Options.IndentationString;
int nextLine = begin; // in #dev coordinates int nextLine = begin; // in #dev coordinates
@ -116,16 +120,16 @@ namespace ICSharpCode.XmlEditor
if (tagStack.Count == 0) if (tagStack.Count == 0)
currentIndentation = ""; currentIndentation = "";
else else
currentIndentation = (string)tagStack.Pop(); currentIndentation = tagStack.Pop();
} }
if (r.NodeType == XmlNodeType.EndElement) { if (r.NodeType == XmlNodeType.EndElement) {
if (tagStack.Count == 0) if (tagStack.Count == 0)
currentIndentation = ""; currentIndentation = "";
else else
currentIndentation = (string)tagStack.Pop(); currentIndentation = tagStack.Pop();
} }
while (r.LineNumber >= nextLine) { // caution: here we compare 1-based and 0-based line numbers while (r.LineNumber > nextLine) { // caution: here we compare 1-based and 0-based line numbers
if (nextLine > end) break; if (nextLine > end) break;
if (lastType == XmlNodeType.CDATA || lastType == XmlNodeType.Comment) { if (lastType == XmlNodeType.CDATA || lastType == XmlNodeType.Comment) {
nextLine++; nextLine++;
@ -138,7 +142,7 @@ namespace ICSharpCode.XmlEditor
string newText; string newText;
// special case: opening tag has closing bracket on extra line: remove one indentation level // special case: opening tag has closing bracket on extra line: remove one indentation level
if (lineText.Trim() == ">") if (lineText.Trim() == ">")
newText = (string)tagStack.Peek() + lineText.Trim(); newText = tagStack.Peek() + lineText.Trim();
else else
newText = currentIndentation + lineText.Trim(); newText = currentIndentation + lineText.Trim();
@ -154,9 +158,9 @@ namespace ICSharpCode.XmlEditor
if (r.NodeType == XmlNodeType.Element) { if (r.NodeType == XmlNodeType.Element) {
tagStack.Push(currentIndentation); tagStack.Push(currentIndentation);
if (r.LineNumber < begin) if (r.LineNumber < begin)
currentIndentation = DocumentUtilitites.GetIndentation(editor.Document, r.LineNumber - 1); currentIndentation = DocumentUtilitites.GetIndentation(editor.Document, editor.Document.PositionToOffset(r.LineNumber, 1));
if (r.Name.Length < 16) if (r.Name.Length < 16)
attribIndent = currentIndentation + new String(' ', 2 + r.Name.Length); attribIndent = currentIndentation + new string(' ', 2 + r.Name.Length);
else else
attribIndent = currentIndentation + tab; attribIndent = currentIndentation + tab;
currentIndentation += tab; currentIndentation += tab;
@ -168,15 +172,12 @@ namespace ICSharpCode.XmlEditor
if (r.LineNumber != startLine) if (r.LineNumber != startLine)
attribIndent = currentIndentation; // change to tab-indentation attribIndent = currentIndentation; // change to tab-indentation
r.MoveToAttribute(r.AttributeCount - 1); r.MoveToAttribute(r.AttributeCount - 1);
while (r.LineNumber >= nextLine) { while (r.LineNumber > nextLine) {
if (nextLine > end) break; if (nextLine > end) break;
// set indentation of 'nextLine' // set indentation of 'nextLine'
IDocumentLine line = document.GetLine(nextLine); IDocumentLine line = document.GetLine(nextLine);
string lineText = line.Text; string newText = attribIndent + line.Text.Trim();
string newText = attribIndent + lineText.Trim(); document.SmartReplaceLine(line, newText);
if (newText != lineText) {
document.Replace(line.Offset, line.Length, newText);
}
nextLine++; nextLine++;
} }
} }
@ -184,6 +185,6 @@ namespace ICSharpCode.XmlEditor
r.Close(); r.Close();
} }
} }
#endregion
} }
} }

21
src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlParser.cs

@ -243,12 +243,15 @@ namespace ICSharpCode.XmlEditor
} }
// Find equals sign. // Find equals sign.
bool foundQuoteChar = false;
for (int i = index; i > elementStartIndex; --i) { for (int i = index; i > elementStartIndex; --i) {
char ch = xml[i]; char ch = xml[i];
if (ch == '=') { if (ch == '=' && foundQuoteChar) {
index = i; index = i;
ignoreEqualsSign = true; ignoreEqualsSign = true;
break; break;
} else if (IsQuoteChar(ch)) {
foundQuoteChar = true;
} }
} }
} else { } else {
@ -364,11 +367,14 @@ namespace ICSharpCode.XmlEditor
// Find equals sign. // Find equals sign.
int equalsSignIndex = -1; int equalsSignIndex = -1;
bool foundQuoteChar = false;
for (int i = index; i > elementStartIndex; --i) { for (int i = index; i > elementStartIndex; --i) {
char ch = xml[i]; char ch = xml[i];
if (ch == '=') { if (ch == '=' && foundQuoteChar) {
equalsSignIndex = i; equalsSignIndex = i;
break; break;
} else if (IsQuoteChar(ch)) {
foundQuoteChar = true;
} }
} }
@ -378,12 +384,12 @@ namespace ICSharpCode.XmlEditor
// Find attribute value. // Find attribute value.
char quoteChar = ' '; char quoteChar = ' ';
bool foundQuoteChar = false; foundQuoteChar = false;
StringBuilder attributeValue = new StringBuilder(); StringBuilder attributeValue = new StringBuilder();
for (int i = equalsSignIndex; i < xml.Length; ++i) { for (int i = equalsSignIndex; i < xml.Length; ++i) {
char ch = xml[i]; char ch = xml[i];
if (!foundQuoteChar) { if (!foundQuoteChar) {
if (ch == '\"' || ch == '\'') { if (IsQuoteChar(ch)) {
quoteChar = ch; quoteChar = ch;
foundQuoteChar = true; foundQuoteChar = true;
} }
@ -391,7 +397,7 @@ namespace ICSharpCode.XmlEditor
if (ch == quoteChar) { if (ch == quoteChar) {
// End of attribute value. // End of attribute value.
return attributeValue.ToString(); return attributeValue.ToString();
} else if (IsAttributeValueChar(ch) || (ch == '\"' || ch == '\'')) { } else if (IsAttributeValueChar(ch) || IsQuoteChar(ch)) {
attributeValue.Append(ch); attributeValue.Append(ch);
} else { } else {
// Invalid character found. // Invalid character found.
@ -783,5 +789,10 @@ namespace ICSharpCode.XmlEditor
} }
return new XmlElementPath(); return new XmlElementPath();
} }
static bool IsQuoteChar(char ch)
{
return (ch == '\"') || (ch == '\'');
}
} }
} }

6
src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlTreeView.cs

@ -134,10 +134,10 @@ namespace ICSharpCode.XmlEditor
protected override void LoadFromPrimary() protected override void LoadFromPrimary()
{ {
IFileDocumentProvider provider = this.PrimaryViewContent as IFileDocumentProvider; IFileDocumentProvider provider = this.PrimaryViewContent as IFileDocumentProvider;
treeViewContainer.LoadXml(provider.GetDocumentForFile(this.PrimaryFile).Text, XmlView.GetProvider(Path.GetExtension(this.PrimaryFileName))); treeViewContainer.LoadXml(provider.GetDocumentForFile(this.PrimaryFile).Text, XmlCodeCompletionBinding.GetProvider(Path.GetExtension(this.PrimaryFileName)));
XmlView view = XmlView.ForFile(this.PrimaryFile); XmlView view = XmlView.ForFile(this.PrimaryFile);
if (view != null) { if (view != null) {
view.CheckIsWellFormed(); XmlView.CheckIsWellFormed(view.TextEditor);
} }
} }
@ -147,7 +147,7 @@ namespace ICSharpCode.XmlEditor
if (!treeViewContainer.IsErrorMessageTextBoxVisible && treeViewContainer.IsDirty) { if (!treeViewContainer.IsErrorMessageTextBoxVisible && treeViewContainer.IsDirty) {
XmlView view = XmlView.ForFile(this.PrimaryFile); XmlView view = XmlView.ForFile(this.PrimaryFile);
if (view != null) { if (view != null) {
view.ReplaceAll(treeViewContainer.Document.OuterXml); XmlView.ReplaceAll(treeViewContainer.Document.OuterXml, view.TextEditor);
ignoreDirtyChange = true; ignoreDirtyChange = true;
treeViewContainer.IsDirty = false; treeViewContainer.IsDirty = false;
ignoreDirtyChange = false; ignoreDirtyChange = false;

122
src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlView.cs

@ -1,7 +1,7 @@
// <file> // <file>
// <copyright see="prj:///doc/copyright.txt"/> // <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/> // <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="sie_pam@gmx.at"/> // <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
@ -59,59 +59,38 @@ namespace ICSharpCode.XmlEditor
public static XmlView ForViewContent(IViewContent view) public static XmlView ForViewContent(IViewContent view)
{ {
if (view == null || view.PrimaryFile == null) if ((view == null) || (view.PrimaryFile == null)) {
return null; return null;
}
return ForFile(view.PrimaryFile); return ForFile(view.PrimaryFile);
} }
static void FileClosedHandler(object sender, EventArgs e) static void FileClosedHandler(object sender, EventArgs e)
{ {
if (sender is OpenedFile) if (sender is OpenedFile) {
mapping.Remove(sender as OpenedFile); mapping.Remove(sender as OpenedFile);
}
} }
public string StylesheetFileName { get; set; } public string StylesheetFileName { get; set; }
public XmlCompletionDataProvider GetProvider() public XmlCompletionDataProvider GetProvider()
{ {
return GetProvider(Path.GetExtension(File.FileName)); return XmlCodeCompletionBinding.GetProvider(Path.GetExtension(File.FileName));
}
public static XmlCompletionDataProvider GetProvider(string extension)
{
string defaultNamespacePrefix;
XmlSchemaCompletionData defaultSchemaCompletionData;
XmlSchemaCompletionDataCollection schemas;
if (PropertyService.DataDirectory != null) {
schemas = XmlSchemaManager.SchemaCompletionDataItems;
defaultNamespacePrefix = XmlSchemaManager.GetNamespacePrefix(extension);
defaultSchemaCompletionData = XmlSchemaManager.GetSchemaCompletionData(extension);
} else {
// for NUnit tests
defaultNamespacePrefix = string.Empty;
schemas = new XmlSchemaCompletionDataCollection();
defaultSchemaCompletionData = null;
}
return new XmlCompletionDataProvider(schemas,
defaultSchemaCompletionData,
defaultNamespacePrefix);
} }
public ITextEditor TextEditor public ITextEditor TextEditor
{ {
get { get {
foreach (IViewContent view in File.RegisteredViewContents) { foreach (IViewContent view in File.RegisteredViewContents) {
ITextEditorProvider provider = view as ITextEditorProvider; ITextEditorProvider provider = view as ITextEditorProvider;
if (provider != null) { if (provider != null) {
IDocument document = provider.GetDocumentForFile(File); IDocument document = provider.GetDocumentForFile(File);
if (document != null) if (document != null) {
return provider.TextEditor; return provider.TextEditor;
}
} }
} }
return null; return null;
} }
} }
@ -121,18 +100,16 @@ namespace ICSharpCode.XmlEditor
get { get {
foreach (IViewContent view in File.RegisteredViewContents) { foreach (IViewContent view in File.RegisteredViewContents) {
XmlTreeView tree = view as XmlTreeView; XmlTreeView tree = view as XmlTreeView;
if (tree != null) if (tree != null) {
return tree; return tree;
}
} }
return null; return null;
} }
} }
public static XmlView ActiveXmlView { public static XmlView ActiveXmlView {
get { get { return XmlView.ForViewContent(WorkbenchSingleton.Workbench.ActiveViewContent); }
return XmlView.ForViewContent(WorkbenchSingleton.Workbench.ActiveViewContent);
}
} }
/// <summary> /// <summary>
@ -207,24 +184,24 @@ namespace ICSharpCode.XmlEditor
/// Checks that the xml in this view is well-formed. /// Checks that the xml in this view is well-formed.
/// </summary> /// </summary>
public bool IsWellFormed { public bool IsWellFormed {
get { get { return CheckIsWellFormed(); }
return CheckIsWellFormed();
}
} }
public bool CheckIsWellFormed() public bool CheckIsWellFormed()
{ {
ITextEditor editor = TextEditor; return CheckIsWellFormed(TextEditor);
}
public static bool CheckIsWellFormed(ITextEditor editor)
{
if (editor == null) return false; if (editor == null) return false;
try { try {
XmlDocument Document = new XmlDocument(); XmlDocument Document = new XmlDocument();
Document.LoadXml(editor.Document.Text); Document.LoadXml(editor.Document.Text);
return true; return true;
} } catch (XmlException ex) {
catch (XmlException ex) {
AddTask(editor.FileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1, TaskType.Error); AddTask(editor.FileName, ex.Message, ex.LinePosition - 1, ex.LineNumber - 1, TaskType.Error);
} } catch (WebException ex) {
catch (WebException ex) {
AddTask(editor.FileName, ex.Message, 0, 0, TaskType.Error); AddTask(editor.FileName, ex.Message, 0, 0, TaskType.Error);
} }
return false; return false;
@ -435,8 +412,7 @@ namespace ICSharpCode.XmlEditor
public string[] InferSchema() public string[] InferSchema()
{ {
ITextEditor editor = TextEditor; ITextEditor editor = TextEditor;
if (editor == null) if (editor == null) return null;
return null;
TaskService.ClearExceptCommentTasks(); TaskService.ClearExceptCommentTasks();
if (IsWellFormed) { if (IsWellFormed) {
@ -457,16 +433,13 @@ namespace ICSharpCode.XmlEditor
/// <summary> /// <summary>
/// Pretty prints the xml. /// Pretty prints the xml.
/// </summary> /// </summary>
public void FormatXml() public static void FormatXml(ITextEditor editor)
{ {
ITextEditor editor = TextEditor; if (editor == null) return;
if (editor == null)
return;
TaskService.ClearExceptCommentTasks(); TaskService.ClearExceptCommentTasks();
if (CheckIsWellFormed(editor)) {
if (IsWellFormed) { ReplaceAll(editor.Document.Text, editor);
ReplaceAll(editor.Document.Text);
} else { } else {
ShowErrorList(); ShowErrorList();
} }
@ -476,13 +449,11 @@ namespace ICSharpCode.XmlEditor
/// Replaces the entire text of the xml view with the xml in the /// Replaces the entire text of the xml view with the xml in the
/// specified. The xml will be formatted. /// specified. The xml will be formatted.
/// </summary> /// </summary>
public void ReplaceAll(string xml) public static void ReplaceAll(string xml, ITextEditor editor)
{ {
ITextEditor editor = TextEditor; if (editor == null) return;
if (editor == null)
return;
string formattedXml = SimpleFormat(IndentedFormat(xml)); string formattedXml = SimpleFormat(IndentedFormat(xml, editor));
editor.Document.Text = formattedXml; editor.Document.Text = formattedXml;
//UpdateFolding(); // TODO : add again when folding is implemented in AvalonEdit //UpdateFolding(); // TODO : add again when folding is implemented in AvalonEdit
} }
@ -502,17 +473,15 @@ namespace ICSharpCode.XmlEditor
/// <returns>A pretty print version of the specified xml. If the /// <returns>A pretty print version of the specified xml. If the
/// string is not well formed xml the original string is returned. /// string is not well formed xml the original string is returned.
/// </returns> /// </returns>
string IndentedFormat(string xml) static string IndentedFormat(string xml, ITextEditor editor)
{ {
string indentedText = string.Empty; string indentedText = string.Empty;
if (editor == null) return indentedText;
ITextEditor editor = TextEditor;
if (editor == null)
return indentedText;
try { try {
XmlTextReader reader = new XmlTextReader(new StringReader(xml)); XmlTextReader reader = new XmlTextReader(new StringReader(xml));
reader.WhitespaceHandling = WhitespaceHandling.None; reader.WhitespaceHandling = WhitespaceHandling.None;
reader.XmlResolver = null;
StringWriter indentedXmlWriter = new StringWriter(); StringWriter indentedXmlWriter = new StringWriter();
XmlTextWriter writer = CreateXmlTextWriter(indentedXmlWriter, editor); XmlTextWriter writer = CreateXmlTextWriter(indentedXmlWriter, editor);
@ -523,7 +492,6 @@ namespace ICSharpCode.XmlEditor
} catch(Exception) { } catch(Exception) {
indentedText = xml; indentedText = xml;
} }
return indentedText; return indentedText;
} }
@ -539,14 +507,14 @@ namespace ICSharpCode.XmlEditor
OutputWindowWriteLine(StringParser.Parse("${res:MainWindow.XmlValidationMessages.ValidationStarted}")); OutputWindowWriteLine(StringParser.Parse("${res:MainWindow.XmlValidationMessages.ValidationStarted}"));
if (IsSchema) { if (IsSchema) {
if (!ValidateSchema()) if (!ValidateSchema()) {
return;
} else {
if (!ValidateAgainstSchema())
return; return;
}
} else if (!ValidateAgainstSchema()) {
return;
} }
OutputWindowWriteLine(string.Empty); OutputWindowWriteLine(String.Empty);
OutputWindowWriteLine(StringParser.Parse("${res:MainWindow.XmlValidationMessages.ValidationSuccess}")); OutputWindowWriteLine(StringParser.Parse("${res:MainWindow.XmlValidationMessages.ValidationSuccess}"));
} }
@ -554,7 +522,7 @@ namespace ICSharpCode.XmlEditor
get { get {
string extension = Path.GetExtension(File.FileName); string extension = Path.GetExtension(File.FileName);
if (extension != null) { if (extension != null) {
return string.Compare(".xsd", extension, StringComparison.OrdinalIgnoreCase) == 0; return String.Compare(".xsd", extension, StringComparison.OrdinalIgnoreCase) == 0;
} }
return false; return false;
} }
@ -567,8 +535,7 @@ namespace ICSharpCode.XmlEditor
bool ValidateAgainstSchema() bool ValidateAgainstSchema()
{ {
ITextEditor editor = TextEditor; ITextEditor editor = TextEditor;
if (editor == null) if (editor == null) return false;
return false;
try { try {
StringReader stringReader = new StringReader(editor.Document.Text); StringReader stringReader = new StringReader(editor.Document.Text);
@ -616,8 +583,7 @@ namespace ICSharpCode.XmlEditor
bool ValidateSchema() bool ValidateSchema()
{ {
ITextEditor editor = TextEditor; ITextEditor editor = TextEditor;
if (editor == null) if (editor == null) return false;
return false;
StringReader stringReader = new StringReader(editor.Document.Text); StringReader stringReader = new StringReader(editor.Document.Text);
XmlTextReader xmlReader = new XmlTextReader(stringReader); XmlTextReader xmlReader = new XmlTextReader(stringReader);
@ -685,8 +651,7 @@ namespace ICSharpCode.XmlEditor
TaskService.ClearExceptCommentTasks(); TaskService.ClearExceptCommentTasks();
ITextEditor editor = TextEditor; ITextEditor editor = TextEditor;
if (editor == null) if (editor == null) return;
return;
if (IsWellFormed) { if (IsWellFormed) {
if (IsValidXsl(xsl)) { if (IsValidXsl(xsl)) {
@ -711,7 +676,7 @@ namespace ICSharpCode.XmlEditor
void ShowTransformOutput(string xml) void ShowTransformOutput(string xml)
{ {
// Pretty print the xml. // Pretty print the xml.
xml = SimpleFormat(IndentedFormat(xml)); xml = SimpleFormat(IndentedFormat(xml, TextEditor));
// Display the output xml. // Display the output xml.
XslOutputView.EditorInstance.Document.Text = xml; XslOutputView.EditorInstance.Document.Text = xml;
@ -818,9 +783,6 @@ namespace ICSharpCode.XmlEditor
/// <summary> /// <summary>
/// Tries to get the filename from the inner exception otherwise returns the default filename. /// Tries to get the filename from the inner exception otherwise returns the default filename.
/// </summary> /// </summary>
/// <param name="ex"></param>
/// <param name="defaultFileName"></param>
/// <returns></returns>
static string GetFileNameFromInnerException(Exception ex, string defaultFileName) static string GetFileNameFromInnerException(Exception ex, string defaultFileName)
{ {
XmlException innerException = ex.InnerException as XmlException; XmlException innerException = ex.InnerException as XmlException;

5
src/AddIns/DisplayBindings/XmlEditor/Project/XmlEditor.csproj

@ -166,6 +166,11 @@
<Name>ICSharpCode.AvalonEdit</Name> <Name>ICSharpCode.AvalonEdit</Name>
<Private>False</Private> <Private>False</Private>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\..\..\Libraries\NRefactory\Project\NRefactory.csproj">
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>
<Name>NRefactory</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj"> <ProjectReference Include="..\..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj">
<Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project> <Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project>
<Name>ICSharpCode.SharpDevelop</Name> <Name>ICSharpCode.SharpDevelop</Name>

2
src/AddIns/DisplayBindings/XmlEditor/Test/Parser/AttributeValueUnderCursorTests.cs

@ -85,6 +85,8 @@ namespace XmlEditor.Tests.Parser
string xaml = "<Test val1=\"{Binding Value, Path=Control}\" />"; string xaml = "<Test val1=\"{Binding Value, Path=Control}\" />";
int offset = "<Test val1=\"{Binding Value, Path=".Length; int offset = "<Test val1=\"{Binding Value, Path=".Length;
Assert.IsTrue(XmlParser.IsInsideAttributeValue(xaml, offset)); Assert.IsTrue(XmlParser.IsInsideAttributeValue(xaml, offset));
Assert.AreEqual("{Binding Value, Path=Control}", XmlParser.GetAttributeValueAtIndex(xaml, offset));
Assert.AreEqual("val1", XmlParser.GetAttributeNameAtIndex(xaml, offset));
} }
[Test] [Test]

66
src/AddIns/DisplayBindings/XmlEditor/XmlEditor.sln

@ -0,0 +1,66 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
# SharpDevelop 3.0.0.3800
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlEditor", "Project\XmlEditor.csproj", "{DCA2703D-250A-463E-A68A-07ED105AE6BD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlEditor.Tests", "Test\XmlEditor.Tests.csproj", "{FC0FE702-A87D-4D70-A9B6-1ECCD611125F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "..\..\..\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj", "{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core", "..\..\..\Main\Core\Project\ICSharpCode.Core.csproj", "{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop", "..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj", "{2748AD25-9C63-4E12-877B-4DCE96FBED54}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core.Presentation", "..\..\..\Main\ICSharpCode.Core.Presentation\ICSharpCode.Core.Presentation.csproj", "{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core.WinForms", "..\..\..\Main\ICSharpCode.Core.WinForms\ICSharpCode.Core.WinForms.csproj", "{857CA1A3-FC88-4BE0-AB6A-D1EE772AB288}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom", "..\..\..\Main\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj", "{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TextEditor", "..\..\..\Libraries\ICSharpCode.TextEditor\Project\ICSharpCode.TextEditor.csproj", "{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DCA2703D-250A-463E-A68A-07ED105AE6BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCA2703D-250A-463E-A68A-07ED105AE6BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DCA2703D-250A-463E-A68A-07ED105AE6BD}.Release|Any CPU.Build.0 = Release|Any CPU
{DCA2703D-250A-463E-A68A-07ED105AE6BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC0FE702-A87D-4D70-A9B6-1ECCD611125F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC0FE702-A87D-4D70-A9B6-1ECCD611125F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC0FE702-A87D-4D70-A9B6-1ECCD611125F}.Release|Any CPU.Build.0 = Release|Any CPU
{FC0FE702-A87D-4D70-A9B6-1ECCD611125F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Release|Any CPU.Build.0 = Release|Any CPU
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}.Release|Any CPU.Build.0 = Release|Any CPU
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2748AD25-9C63-4E12-877B-4DCE96FBED54}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2748AD25-9C63-4E12-877B-4DCE96FBED54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2748AD25-9C63-4E12-877B-4DCE96FBED54}.Release|Any CPU.Build.0 = Release|Any CPU
{2748AD25-9C63-4E12-877B-4DCE96FBED54}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}.Release|Any CPU.Build.0 = Release|Any CPU
{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{857CA1A3-FC88-4BE0-AB6A-D1EE772AB288}.Debug|Any CPU.Build.0 = Debug|Any CPU
{857CA1A3-FC88-4BE0-AB6A-D1EE772AB288}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{857CA1A3-FC88-4BE0-AB6A-D1EE772AB288}.Release|Any CPU.Build.0 = Release|Any CPU
{857CA1A3-FC88-4BE0-AB6A-D1EE772AB288}.Release|Any CPU.ActiveCfg = Release|Any CPU
{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Release|Any CPU.Build.0 = Release|Any CPU
{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}.Release|Any CPU.Build.0 = Release|Any CPU
{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
EndGlobal

19
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj

@ -170,24 +170,31 @@
<Compile Include="Src\Visualizers\Graph\DebuggerVisualizerException.cs" /> <Compile Include="Src\Visualizers\Graph\DebuggerVisualizerException.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\CanvasLocationAdapter.cs" /> <Compile Include="Src\Visualizers\Graph\Drawing\CanvasLocationAdapter.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\GraphDrawer.cs" /> <Compile Include="Src\Visualizers\Graph\Drawing\GraphDrawer.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\NodeControl.xaml.cs" /> <Compile Include="Src\Visualizers\Graph\Drawing\NodeControl.xaml.cs">
<DependentUpon>NodeControl.xaml</DependentUpon>
</Compile>
<Compile Include="Src\Visualizers\Graph\ExpandedNodes.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\GraphDiff.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\GraphDiff.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\GraphMatcher.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\GraphMatcher.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedEdge.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\PositionedEdge.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedGraph.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\PositionedGraph.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedNode.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\PositionedNode.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\DotGraph.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\PositionedNodeProperty.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedPropertyEventArgs.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\BoxDotFormatter.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\DotFormatter.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\IdGenerator.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\IdGenerator.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\LayoutDirection.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\LayoutDirection.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoDoubleFormatter.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoEdgeRouter.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoEdgeRouter.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoPositionTransform.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoPositionTransform.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoProcess.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoProcess.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\RecordDotFormatter.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeEdge.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeEdge.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeLayouter.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeLayouter.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNode.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNode.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNodeLR.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNodeLR.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNodeTB.cs" /> <Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNodeTB.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraphVisualizerViewContent.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\NamedEdge.cs" /> <Compile Include="Src\Visualizers\Graph\ObjectGraph\NamedEdge.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectEdge.cs" /> <Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectEdge.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectGraph.cs" /> <Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectGraph.cs" />
@ -199,11 +206,6 @@
<Compile Include="Src\Visualizers\Graph\Utils\DictionaryExtensions.cs" /> <Compile Include="Src\Visualizers\Graph\Utils\DictionaryExtensions.cs" />
<Compile Include="Src\Visualizers\Graph\Utils\Lookup.cs" /> <Compile Include="Src\Visualizers\Graph\Utils\Lookup.cs" />
<Compile Include="Src\Visualizers\Graph\Utils\LookupValueCollection.cs" /> <Compile Include="Src\Visualizers\Graph\Utils\LookupValueCollection.cs" />
<Compile Include="Src\Visualizers\Graph\VisualizerWinFormsControl.cs" />
<Compile Include="Src\Visualizers\Graph\VisualizerWinFormsControl.Designer.cs" />
<Compile Include="Src\Visualizers\Graph\VisualizerWPFControl.xaml.cs">
<DependentUpon>VisualizerWPFControl.xaml</DependentUpon>
</Compile>
<Compile Include="Src\Visualizers\Graph\VisualizerWPFWindow.xaml.cs"> <Compile Include="Src\Visualizers\Graph\VisualizerWPFWindow.xaml.cs">
<DependentUpon>VisualizerWPFWindow.xaml</DependentUpon> <DependentUpon>VisualizerWPFWindow.xaml</DependentUpon>
</Compile> </Compile>
@ -297,7 +299,6 @@
<Folder Include="Src\Visualizers\Graph\Utils" /> <Folder Include="Src\Visualizers\Graph\Utils" />
<Folder Include="Src\Visualizers\PresentationBindings" /> <Folder Include="Src\Visualizers\PresentationBindings" />
<Page Include="Src\Visualizers\Graph\Drawing\NodeControl.xaml" /> <Page Include="Src\Visualizers\Graph\Drawing\NodeControl.xaml" />
<Page Include="Src\Visualizers\Graph\VisualizerWPFControl.xaml" />
<Page Include="Src\Visualizers\Graph\VisualizerWPFWindow.xaml" /> <Page Include="Src\Visualizers\Graph\VisualizerWPFWindow.xaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

1
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml

@ -16,6 +16,7 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>

177
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/NodeControl.xaml.cs

@ -4,6 +4,7 @@
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/> // <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using Debugger.AddIn.Visualizers.Graph.Layout;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
@ -20,59 +21,133 @@ using Debugger.AddIn.Visualizers.Graph;
namespace Debugger.AddIn.Visualizers.Graph.Drawing namespace Debugger.AddIn.Visualizers.Graph.Drawing
{ {
/// <summary> /// <summary>
/// UserControl used to display ObjectNode. /// UserControl used to display Positione.
/// </summary> /// </summary>
public partial class NodeControl : UserControl public partial class NodeControl : UserControl
{ {
public NodeControl() /// <summary>
{ /// Creates new NodeControl displaying PositionedNode.
InitializeComponent(); /// </summary>
} /// <param name="graphNode">PositionedNode displayed by the control.</param>
public NodeControl(PositionedNode graphNode) : this()
{
//this.initializeWithGraphNode(graphNode);
this.GraphNode = graphNode;
}
public NodeControl()
{
InitializeComponent();
}
public event EventHandler<PositionedPropertyEventArgs> Expanded;
public event EventHandler<PositionedPropertyEventArgs> Collapsed;
private ObjectNode node; private PositionedNode node;
/// <summary> /// <summary>
/// ObjectNode that this control displays. /// ObjectNode that this control displays.
/// </summary> /// </summary>
public ObjectNode GraphNode public PositionedNode GraphNode
{ {
get get
{ {
return node; return node;
} }
set private set
{ {
node = value; this.node = value;
int row = 0; }
// dynamically create TextBlocks and insert them to the 2-column propertyGrid }
foreach (var property in node.Properties)
{ public void AddProperty(PositionedNodeProperty property)
propertyGrid.RowDefinitions.Add(new RowDefinition()); {
int nRow = propertyGrid.RowDefinitions.Count;
var row = new RowDefinition();
propertyGrid.RowDefinitions.Add(row);
if (!property.IsAtomic && !property.IsNull)
{
Button btnExpandCollapse = new Button();
btnExpandCollapse.Tag = property;
btnExpandCollapse.Content = property.IsExpanded ? "-" : "+";
btnExpandCollapse.Width = 20;
propertyGrid.Children.Add(btnExpandCollapse);
Grid.SetRow(btnExpandCollapse, nRow);
Grid.SetColumn(btnExpandCollapse, 0);
btnExpandCollapse.Click += new RoutedEventHandler(btnExpandCollapse_Click);
}
TextBlock txtName = createTextBlock(property.Name); TextBlock txtName = createTextBlock(property.Name);
propertyGrid.Children.Add(txtName); propertyGrid.Children.Add(txtName);
Grid.SetRow(txtName, row); Grid.SetRow(txtName, nRow);
Grid.SetColumn(txtName, 0); Grid.SetColumn(txtName, 1);
TextBlock txtValue = createTextBlock(property.Value); TextBlock txtValue = createTextBlock(property.Value);
propertyGrid.Children.Add(txtValue); propertyGrid.Children.Add(txtValue);
Grid.SetRow(txtValue, row); Grid.SetRow(txtValue, nRow);
Grid.SetColumn(txtValue, 1); Grid.SetColumn(txtValue, 2);
}
/*public void Measure()
{
this.Measure();
int nRow = 0;
// dynamically create TextBlocks and insert them to the 2-column propertyGrid
foreach (var property in node.Properties)
{
row++; nRow++;
} }
} }*/
}
void btnExpandCollapse_Click(object sender, RoutedEventArgs e)
/// <summary> {
/// Creates TextBlock with given text. Button buttonClicked = ((Button)sender);
/// </summary> var property = (PositionedNodeProperty)buttonClicked.Tag;
private TextBlock createTextBlock(string text)
{ property.IsExpanded = !property.IsExpanded;
TextBlock newTextblock = new TextBlock(); buttonClicked.Content = property.IsExpanded ? "-" : "+";
newTextblock.Text = text; if (property.IsExpanded)
newTextblock.Padding = new Thickness(4); {
return newTextblock; OnPropertyExpanded(property);
} }
} else
{
OnPropertyCollapsed(property);
}
}
/// <summary>
/// Creates TextBlock with given text.
/// </summary>
private TextBlock createTextBlock(string text)
{
TextBlock newTextblock = new TextBlock();
newTextblock.Text = text;
newTextblock.Padding = new Thickness(4);
return newTextblock;
}
#region event helpers
protected virtual void OnPropertyExpanded(PositionedNodeProperty property)
{
if (this.Expanded != null)
{
this.Expanded(this, new PositionedPropertyEventArgs(property));
}
}
protected virtual void OnPropertyCollapsed(PositionedNodeProperty property)
{
if (this.Collapsed != null)
{
this.Collapsed(this, new PositionedPropertyEventArgs(property));
}
}
#endregion
}
} }

38
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ExpandedNodes.cs

@ -0,0 +1,38 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Remembers which expressions the user has expanded.
/// </summary>
public class ExpandedNodes
{
private Dictionary<string, bool> expandedNodes = new Dictionary<string, bool>();
public ExpandedNodes()
{
}
public bool IsExpanded(string expression)
{
return expandedNodes.ContainsKey(expression) && (expandedNodes[expression] == true);
}
public void SetExpanded(string expression)
{
expandedNodes[expression] = true;
}
public void SetCollapsed(string expression)
{
expandedNodes[expression] = false;
}
}
}

2
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs

@ -94,7 +94,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
private bool isSameAddress(PositionedNode node1, PositionedNode node2) private bool isSameAddress(PositionedNode node1, PositionedNode node2)
{ {
return node1.ObjectNode.PermanentReference.GetObjectAddress() == node2.ObjectNode.PermanentReference.GetObjectAddress(); return node1.ObjectNode.DebuggerValue.GetObjectAddress() == node2.ObjectNode.DebuggerValue.GetObjectAddress();
} }
} }
} }

2
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs

@ -13,7 +13,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary> /// <summary>
/// Edge with position information. /// Edge with position information.
/// </summary> /// </summary>
public class PositionedEdge : NamedEdge<PositionedNode> public class PositionedEdge : NamedEdge<PositionedNodeProperty, PositionedNode>
{ {
private IList<Point> splinePoints = new List<Point>(); private IList<Point> splinePoints = new List<Point>();

7
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedGraph.cs

@ -16,7 +16,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary> /// </summary>
public class PositionedGraph public class PositionedGraph
{ {
internal List<PositionedNode> nodes = new List<PositionedNode>(); private List<PositionedNode> nodes = new List<PositionedNode>();
public System.Windows.Rect BoundingRect public System.Windows.Rect BoundingRect
{ {
@ -39,6 +39,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
get { return nodes; } get { return nodes; }
} }
internal void AddNode(PositionedNode node)
{
this.nodes.Add(node);
}
/// <summary> /// <summary>
/// All edges in the graph. /// All edges in the graph.
/// </summary> /// </summary>

79
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs

@ -12,20 +12,69 @@ using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout namespace Debugger.AddIn.Visualizers.Graph.Layout
{ {
/// <summary> /// <summary>
/// Node with added position information. /// ObjectNode with added position information.
/// </summary> /// </summary>
public class PositionedNode public class PositionedNode
{ {
private ObjectNode objectNode; private ObjectNode objectNode;
/// <summary>
/// Underlying ObjectNode.
/// </summary>
public ObjectNode ObjectNode public ObjectNode ObjectNode
{ {
get { return objectNode; } get { return objectNode; }
} }
public PositionedNode(NodeControl nodeVisualControl, ObjectNode objectNode) public event EventHandler<PositionedPropertyEventArgs> Expanded;
public event EventHandler<PositionedPropertyEventArgs> Collapsed;
private List<PositionedNodeProperty> properties = new List<PositionedNodeProperty>();
public List<PositionedNodeProperty> Properties
{
get
{
return this.properties;
}
}
public PositionedNodeProperty AddProperty(ObjectProperty objectProperty, bool isExpanded)
{
var newProperty = new PositionedNodeProperty(objectProperty, this);
newProperty.IsExpanded = isExpanded;
this.Properties.Add(newProperty);
this.nodeVisualControl.AddProperty(newProperty);
return newProperty;
}
/// <summary>
/// Creates new PositionedNode.
/// </summary>
/// <param name="objectNode">Underlying ObjectNode.</param>
public PositionedNode(ObjectNode objectNode)
{ {
this.nodeVisualControl = nodeVisualControl;
this.objectNode = objectNode; this.objectNode = objectNode;
this.nodeVisualControl = new NodeControl(this); // display
this.nodeVisualControl.Expanded += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_Expanded);
this.nodeVisualControl.Collapsed += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_Collapsed);
}
private void NodeVisualControl_Expanded(object sender, PositionedPropertyEventArgs e)
{
// propagage event
OnPropertyExpanded(this, e);
}
private void NodeVisualControl_Collapsed(object sender, PositionedPropertyEventArgs e)
{
// propagate event
OnPropertyCollapsed(this, e);
}
public void Measure()
{
this.nodeVisualControl.Measure(new Size(500, 500));
} }
public double Left { get; set; } public double Left { get; set; }
@ -67,8 +116,30 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{ {
get get
{ {
return new PositionedEdge[]{}; foreach (PositionedNodeProperty property in this.Properties)
{
if (property.Edge != null)
yield return property.Edge;
}
}
}
#region event helpers
protected virtual void OnPropertyExpanded(object sender, PositionedPropertyEventArgs propertyArgs)
{
if (this.Expanded != null)
{
this.Expanded(sender, propertyArgs);
}
}
protected virtual void OnPropertyCollapsed(object sender, PositionedPropertyEventArgs propertyArgs)
{
if (this.Collapsed != null)
{
this.Collapsed(sender, propertyArgs);
} }
} }
#endregion
} }
} }

76
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNodeProperty.cs

@ -0,0 +1,76 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// <see cref="ObjectProperty"/> with outgoing <see cref="PositionedEdge"/>.
/// </summary>
public class PositionedNodeProperty
{
/// <summary>
/// Creates new PositionedNodeProperty.
/// </summary>
/// <param name="objectProperty">Underlying <see cref="ObjectProperty"/></param>
public PositionedNodeProperty(ObjectProperty objectProperty, PositionedNode containingNode)
{
this.objectProperty = objectProperty;
this.containingNode = containingNode;
}
public bool IsExpanded { get; set; }
private ObjectProperty objectProperty;
/// <summary>
/// Underlying <see cref="ObjectProperty"/>.
/// </summary>
public ObjectProperty ObjectProperty
{
get { return this.objectProperty; }
}
private PositionedNode containingNode;
/// <summary>
/// <see cref="PositionedNode"/> which contains this Property.
/// </summary>
public PositionedNode ContainingNode
{
get { return this.containingNode; }
}
/// <summary>
/// Edge outgoing from this property to another <see cref="PositionedNode"/>.
/// </summary>
public PositionedEdge Edge { get; set; }
/// <summary>
/// e.g. "Age"
/// </summary>
public string Name { get { return this.objectProperty.Name; } }
/// <summary>
/// e.g. "19"
/// </summary>
public string Value { get { return this.objectProperty.Value; } }
/// <summary>
/// Full Debugger expression used to obtain value of this property.
/// </summary>
public Debugger.Expressions.Expression Expression { get { return this.objectProperty.Expression; } }
/// <summary>
/// Is this property of atomic type? (int, string, etc.)
/// </summary>
public bool IsAtomic { get { return this.objectProperty.IsAtomic; } }
/// <summary>
/// Is the value of this property null?
/// </summary>
public bool IsNull { get { return this.objectProperty.IsNull; } }
}
}

25
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedPropertyEventArgs.cs

@ -0,0 +1,25 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// EventArgs carrying <see cref="PositionedNodeProperty"/>.
/// </summary>
public class PositionedPropertyEventArgs : EventArgs
{
private PositionedNodeProperty property;
public PositionedPropertyEventArgs(PositionedNodeProperty property)
{
this.property = property;
}
public PositionedNodeProperty Property { get { return this.property; } }
}
}

44
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/BoxDotFormatter.cs

@ -0,0 +1,44 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Text;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// <see cref="DotFormatter"/> that treats node as a atomic "box". Edges go from box to box.
/// </summary>
public class BoxDotFormatter : DotFormatter
{
public BoxDotFormatter(PositionedGraph posGraph) : base(posGraph)
{
}
protected override void appendPosNode(PositionedNode node, StringBuilder builder)
{
string nodeName = genId.GetNextId().ToString();
nodeNames[node] = nodeName;
Rect neatoInput = transform.NodeToNeatoInput(node);
string dotFormatNode =
string.Format(this.neatoDoubleFormatter,
"{0} [pos=\"{1},{2}!\" width=\"{3}\" height=\"{4}\"];",
nodeName, neatoInput.Location.X, neatoInput.Location.Y, neatoInput.Width, neatoInput.Height);
builder.AppendLine(dotFormatNode);
}
protected override void appendPosEdge(PositionedEdge edge, StringBuilder builder)
{
string sourceNodeName = nodeNames[edge.Source.ContainingNode];
string targetNodeName = nodeNames[edge.Target];
builder.AppendLine(string.Format("{0} -> {1}", sourceNodeName, targetNodeName));
}
}
}

82
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/DotGraph.cs → src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/DotFormatter.cs

@ -17,24 +17,24 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary> /// <summary>
/// Converts <see cref="PositionedGraph/> to Graphviz's string (dot format) and back (from plain format). /// Converts <see cref="PositionedGraph/> to Graphviz's string (dot format) and back (from plain format).
/// </summary> /// </summary>
public class DotGraph public abstract class DotFormatter
{ {
private PositionedGraph posGraph; protected PositionedGraph posGraph;
NeatoPositionTransform transform; protected NeatoPositionTransform transform;
// state (node and edge names) needed for converting back protected NeatoDoubleFormatter neatoDoubleFormatter = new NeatoDoubleFormatter();
private Dictionary<PositionedNode, string> nodeNames = new Dictionary<PositionedNode, string>();
private Dictionary<PositionedEdge, string> edgeNames = new Dictionary<PositionedEdge, string>();
private CultureInfo formatCulture = CultureInfo.GetCultureInfo("en-US"); // state (node and edge names) needed for parsing back
protected Dictionary<PositionedNode, string> nodeNames = new Dictionary<PositionedNode, string>();
protected Dictionary<PositionedEdge, string> edgeNames = new Dictionary<PositionedEdge, string>();
/// <summary> /// <summary>
/// Used for generating node and edge names. /// Used for generating node and edge names.
/// </summary> /// </summary>
private IdGenerator genId = new IdGenerator(); protected IdGenerator genId = new IdGenerator();
public DotGraph(PositionedGraph posGraph) public DotFormatter(PositionedGraph posGraph)
{ {
if (posGraph.Nodes.Count() == 0) if (posGraph.Nodes.Count() == 0)
{ {
@ -44,54 +44,43 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
this.transform = new NeatoPositionTransform(this.posGraph.BoundingRect); this.transform = new NeatoPositionTransform(this.posGraph.BoundingRect);
} }
/// <summary> protected abstract void appendPosNode(PositionedNode node, StringBuilder builder);
/// Gets Graphviz's dot format string for the positioned graph.
/// </summary> protected abstract void appendPosEdge(PositionedEdge edge, StringBuilder builder);
public string DotGraphString
protected virtual string getGraphHeader()
{ {
get return "digraph G { node [shape = box];";
{
StringBuilder dotStringBuilder = new StringBuilder("digraph G { node [shape = box];");
foreach (PositionedNode posNode in this.posGraph.Nodes)
{
appendPosNode(posNode, dotStringBuilder);
}
foreach (PositionedEdge posEdge in this.posGraph.Edges)
{
appendPosEdge(posEdge, dotStringBuilder);
}
dotStringBuilder.AppendLine("}");
return dotStringBuilder.ToString();
}
} }
private void appendPosNode(PositionedNode node, StringBuilder builder) protected virtual string getGraphFooter()
{ {
string nodeName = genId.GetNextId().ToString(); return "}";
nodeNames[node] = nodeName;
Rect neatoInput = transform.NodeToNeatoInput(node);
string dotFormatNode =
string.Format(formatCulture,
"{0} [pos=\"{1},{2}!\" width=\"{3}\" height=\"{4}\"];",
nodeName, neatoInput.Location.X, neatoInput.Location.Y, neatoInput.Width, neatoInput.Height);
builder.AppendLine(dotFormatNode);
} }
private void appendPosEdge(PositionedEdge edge, StringBuilder builder) /// <summary>
/// Gets Graphviz's dot format string for wrapped positioned graph.
/// </summary>
public string OutputGraphInDotFormat()
{ {
string sourceNodeName = nodeNames[edge.SourceNode]; StringBuilder dotStringBuilder = new StringBuilder(getGraphHeader());
string targetNodeName = nodeNames[edge.TargetNode];
foreach (PositionedNode posNode in this.posGraph.Nodes)
{
appendPosNode(posNode, dotStringBuilder);
}
foreach (PositionedEdge posEdge in this.posGraph.Edges)
{
appendPosEdge(posEdge, dotStringBuilder);
}
builder.AppendLine(string.Format("{0} -> {1}", sourceNodeName, targetNodeName)); dotStringBuilder.AppendLine(getGraphFooter());
return dotStringBuilder.ToString();
} }
private bool validateSplinePoints(PositionedEdge edge) private bool validateSplinePoints(PositionedEdge edge)
{ {
// must have correct number of points: one start point and 3 points for every bezier segment // must have correct number of points: one start point and 3 points per bezier segment
return ((edge.SplinePoints.Count - 1) % 3) == 0; return ((edge.SplinePoints.Count - 1) % 3) == 0;
} }
@ -112,6 +101,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
transform.NeatoShiftX = neatoFirstNodePos.X - firstNodePosOur.X; transform.NeatoShiftX = neatoFirstNodePos.X - firstNodePosOur.X;
transform.NeatoShiftY = neatoFirstNodePos.Y - firstNodePosOur.Y; transform.NeatoShiftY = neatoFirstNodePos.Y - firstNodePosOur.Y;
// assume that edges on output are in the same order as posGraph.Edges (!)
foreach (PositionedEdge posEdge in posGraph.Edges) foreach (PositionedEdge posEdge in posGraph.Edges)
{ {
skipAfterPattern(reader, "edge "); skipAfterPattern(reader, "edge ");
@ -146,7 +136,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
private double readDouble(TextReader reader) private double readDouble(TextReader reader)
{ {
return double.Parse(readWord(reader), formatCulture); return double.Parse(readWord(reader), this.neatoDoubleFormatter.DoubleCulture);
} }
private int readInt(TextReader reader) private int readInt(TextReader reader)

39
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoDoubleFormatter.cs

@ -0,0 +1,39 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Globalization;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// When used as IFormatProvider in string.Format, formats doubles to be suitable for Neato.
/// </summary>
public class NeatoDoubleFormatter : IFormatProvider, ICustomFormatter
{
private CultureInfo doubleCulture = CultureInfo.GetCultureInfo("en-US");
/// <summary>
/// CultureInfo used for formatting and parsing doubles (en-US).
/// </summary>
public CultureInfo DoubleCulture
{
get { return this.doubleCulture; }
}
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
return string.Format(doubleCulture, "{0:0.000}", arg);
}
}
}

13
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoEdgeRouter.cs

@ -30,14 +30,19 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <returns><see cref="PositionedGraph" /> with preserved node positions and calculated edge positions.</returns> /// <returns><see cref="PositionedGraph" /> with preserved node positions and calculated edge positions.</returns>
public PositionedGraph CalculateEdges(PositionedGraph graphWithNodesPositioned) public PositionedGraph CalculateEdges(PositionedGraph graphWithNodesPositioned)
{ {
DotGraph dotGraph = new DotGraph(graphWithNodesPositioned); DotFormatter dotFormatter = new RecordDotFormatter(graphWithNodesPositioned);
// start Neato.exe
NeatoProcess neatoProcess = NeatoProcess.Start(); NeatoProcess neatoProcess = NeatoProcess.Start();
// convert PosGraph to .dot string, pass to neato.exe
string dotGraphStringWithPositions = neatoProcess.CalculatePositions(dotGraph.DotGraphString); // convert PosGraph to .dot string
string dotGraphString = dotFormatter.OutputGraphInDotFormat();
// pass to neato.exe
string dotGraphStringWithPositions = neatoProcess.CalculatePositions(dotGraphString);
// parse edge positions from neato's plain output format // parse edge positions from neato's plain output format
PositionedGraph result = dotGraph.ParseEdgePositions(dotGraphStringWithPositions); PositionedGraph result = dotFormatter.ParseEdgePositions(dotGraphStringWithPositions);
return result; return result;
} }

6
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoProcess.cs

@ -5,6 +5,7 @@
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using System; using System;
using System.IO;
using System.Text; using System.Text;
namespace Debugger.AddIn.Visualizers.Graph.Layout namespace Debugger.AddIn.Visualizers.Graph.Layout
@ -72,6 +73,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <returns>Same graph in Graphviz plain with position information added.</returns> /// <returns>Same graph in Graphviz plain with position information added.</returns>
public string CalculatePositions(string dotGraph) public string CalculatePositions(string dotGraph)
{ {
/*using (var writer = new StreamWriter("logIn.gv"))
{
writer.Write(dotGraph);
}*/
neatoProcess.StandardInput.Write(dotGraph); neatoProcess.StandardInput.Write(dotGraph);
neatoProcess.StandardInput.Flush(); neatoProcess.StandardInput.Flush();
neatoProcess.StandardInput.Close(); neatoProcess.StandardInput.Close();

68
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/RecordDotFormatter.cs

@ -0,0 +1,68 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// <see cref="DotFormatter"/> that treats nodes as records of properties.
/// Edges start at property, end at node.
/// </summary>
public class RecordDotFormatter : DotFormatter
{
private Dictionary<PositionedNodeProperty, string> propertyIds = new Dictionary<PositionedNodeProperty, string>();
public RecordDotFormatter(PositionedGraph posGraph) : base(posGraph)
{
}
protected override string getGraphHeader()
{
return "digraph G { rankdir=LR; node [shape = record];";
}
protected override void appendPosNode(PositionedNode node, StringBuilder builder)
{
string nodeName = genId.GetNextId().ToString();
nodeNames[node] = nodeName;
Rect neatoInput = transform.NodeToNeatoInput(node);
StringBuilder recordLabel = new StringBuilder();
for (int i = 0; i < node.Properties.Count; i++)
{
string propertyId = "f" + genId.GetNextId().ToString();
propertyIds[node.Properties[i]] = propertyId;
recordLabel.Append(string.Format("<{0}> l", propertyId));
if (i < node.Properties.Count - 1)
{
recordLabel.Append("|");
}
}
string dotFormatNode =
string.Format(this.neatoDoubleFormatter,
"{0} [pos=\"{1},{2}!\" width=\"{3}\" height=\"{4}\" label=\"{5}\"];",
nodeName,
neatoInput.Location.X, neatoInput.Location.Y, neatoInput.Width, neatoInput.Height,
recordLabel.ToString());
builder.AppendLine(dotFormatNode);
}
protected override void appendPosEdge(PositionedEdge edge, StringBuilder builder)
{
string sourceNodeName = nodeNames[edge.Source.ContainingNode];
string sourcePropertyName = propertyIds[edge.Source];
string targetNodeName = nodeNames[edge.Target];
builder.AppendLine(string.Format("{0}:{1} -> {2}", sourceNodeName, sourcePropertyName, targetNodeName));
}
}
}

8
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeEdge.cs

@ -9,9 +9,13 @@ using System;
namespace Debugger.AddIn.Visualizers.Graph.Layout namespace Debugger.AddIn.Visualizers.Graph.Layout
{ {
/// <summary> /// <summary>
/// Description of TreeEdge. /// Edge in the tree-layouted <see cref="PositionedGraph"/>.
/// </summary> /// </summary>
public class TreeEdge : PositionedEdge public class TreeGraphEdge : PositionedEdge
{ {
/// <summary>
/// Is this a main edges making up the tree?
/// </summary>
public bool IsTreeEdge { get; set; }
} }
} }

36
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs

@ -36,7 +36,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary> /// </summary>
/// <param name="objectGraph"></param> /// <param name="objectGraph"></param>
/// <returns></returns> /// <returns></returns>
public PositionedGraph CalculateLayout(ObjectGraph objectGraph, LayoutDirection direction) public PositionedGraph CalculateLayout(ObjectGraph objectGraph, LayoutDirection direction, ExpandedNodes expandedNodes)
{ {
layoutDirection = direction; layoutDirection = direction;
@ -44,7 +44,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
treeNodeFor = new Dictionary<ObjectNode, TreeNode>(); treeNodeFor = new Dictionary<ObjectNode, TreeNode>();
seenNodes = new Dictionary<ObjectNode, object>(); seenNodes = new Dictionary<ObjectNode, object>();
TreeNode tree = buildTreeRecursive(objectGraph.Root); TreeNode tree = buildTreeRecursive(objectGraph.Root, expandedNodes);
calculateNodePosRecursive(tree, 0, 0); calculateNodePosRecursive(tree, 0, 0);
var neatoRouter = new NeatoEdgeRouter(); var neatoRouter = new NeatoEdgeRouter();
@ -53,39 +53,45 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
return resultGraph; return resultGraph;
} }
private TreeNode buildTreeRecursive(ObjectNode objectGraphNode) private TreeNode buildTreeRecursive(ObjectNode objectGraphNode, ExpandedNodes expandedNodes)
{ {
seenNodes.Add(objectGraphNode, null); seenNodes.Add(objectGraphNode, null);
NodeControl nodeVisualControl = new NodeControl(); TreeNode newTreeNode = TreeNode.Create(this.layoutDirection, objectGraphNode);
nodeVisualControl.GraphNode = objectGraphNode;
nodeVisualControl.Measure(new Size(500, 500));
TreeNode newTreeNode = TreeNode.Create(this.layoutDirection, nodeVisualControl, objectGraphNode);
newTreeNode.HorizontalMargin = horizNodeMargin; newTreeNode.HorizontalMargin = horizNodeMargin;
newTreeNode.VerticalMargin = vertNodeMargin; newTreeNode.VerticalMargin = vertNodeMargin;
resultGraph.nodes.Add(newTreeNode); resultGraph.AddNode(newTreeNode);
treeNodeFor[objectGraphNode] = newTreeNode; treeNodeFor[objectGraphNode] = newTreeNode;
double subtreeSize = 0; double subtreeSize = 0;
foreach (ObjectProperty property in objectGraphNode.ComplexProperties) foreach (ObjectProperty property in objectGraphNode.Properties)
{ {
if (property.TargetNode != null) if (property.TargetNode != null)
{ {
ObjectNode neighbor = property.TargetNode; ObjectNode neighbor = property.TargetNode;
TreeNode targetTreeNode = null;
bool newEdgeIsTreeEdge = false;
if (seenNodes.ContainsKey(neighbor)) if (seenNodes.ContainsKey(neighbor))
{ {
newTreeNode.AdditionalNeighbors.Add(new TreeEdge { Name = property.Name, SourceNode = newTreeNode, TargetNode = treeNodeFor[neighbor]}); targetTreeNode = treeNodeFor[neighbor];
newEdgeIsTreeEdge = false;
} }
else else
{ {
TreeNode newChild = buildTreeRecursive(neighbor); targetTreeNode = buildTreeRecursive(neighbor, expandedNodes);
newTreeNode.ChildEdges.Add(new TreeEdge { Name = property.Name, SourceNode = newTreeNode, TargetNode = newChild}); newEdgeIsTreeEdge = true;
subtreeSize += targetTreeNode.SubtreeSize;
subtreeSize += newChild.SubtreeSize;
} }
var posNodeProperty = newTreeNode.AddProperty(property, expandedNodes.IsExpanded(property.Expression.Code));
posNodeProperty.Edge = new TreeGraphEdge { IsTreeEdge = newEdgeIsTreeEdge, Name = property.Name, Source = posNodeProperty, Target = targetTreeNode };
}
else
{
// property.Edge stays null
newTreeNode.AddProperty(property, expandedNodes.IsExpanded(property.Expression.Code));
} }
} }
newTreeNode.Measure();
subtreeSize = Math.Max(newTreeNode.LateralSizeWithMargin, subtreeSize); subtreeSize = Math.Max(newTreeNode.LateralSizeWithMargin, subtreeSize);
newTreeNode.SubtreeSize = subtreeSize; newTreeNode.SubtreeSize = subtreeSize;

50
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs

@ -11,18 +11,19 @@ using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout namespace Debugger.AddIn.Visualizers.Graph.Layout
{ {
/// <summary> /// <summary>
/// Abstract ancestor of TreeNodeLR and TreeNodeTB. /// Node in tree-layouted <see cref="PositionedGraph"/>.
/// This is the abstract ancestor of TreeNodeLR and TreeNodeTB.
/// There are 2 dimensions - "main" and "lateral". /// There are 2 dimensions - "main" and "lateral".
/// Main dimension is the dimension in which the graph depth grows (vertical when TB, horizontal when LR). /// Main dimension is the dimension in which the graph depth grows (vertical when TB, horizontal when LR).
/// Lateral dimension is the other dimension. Siblings are placed next to each other in Lateral dimension. /// Lateral dimension is the other dimension. Siblings are placed next to each other in Lateral dimension.
/// </summary> /// </summary>
public abstract class TreeNode : PositionedNode public abstract class TreeNode : PositionedNode
{ {
public static TreeNode Create(LayoutDirection direction, NodeControl nodeVisualControl, ObjectNode objectNode) public static TreeNode Create(LayoutDirection direction, ObjectNode objectNode)
{ {
switch (direction) { switch (direction) {
case LayoutDirection.TopBottom: return new TreeNodeTB(nodeVisualControl, objectNode); case LayoutDirection.TopBottom: return new TreeNodeTB(objectNode);
case LayoutDirection.LeftRight: return new TreeNodeLR(nodeVisualControl, objectNode); case LayoutDirection.LeftRight: return new TreeNodeLR(objectNode);
default: throw new DebuggerVisualizerException("Unsupported layout direction: " + direction.ToString()); default: throw new DebuggerVisualizerException("Unsupported layout direction: " + direction.ToString());
} }
} }
@ -30,7 +31,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
public double HorizontalMargin { get; set; } public double HorizontalMargin { get; set; }
public double VerticalMargin { get; set; } public double VerticalMargin { get; set; }
protected TreeNode(NodeControl nodeVisualControl, ObjectNode objectNode) : base(nodeVisualControl, objectNode) protected TreeNode(ObjectNode objectNode) : base(objectNode)
{ {
} }
@ -51,12 +52,17 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
public abstract double MainMargin { get; } public abstract double MainMargin { get; }
public abstract double LateralMargin { get; } public abstract double LateralMargin { get; }
private List<TreeEdge> childs = new List<TreeEdge>(); public IEnumerable<PositionedEdge> ChildEdges
public List<TreeEdge> ChildEdges
{ {
get get
{ {
return childs; foreach (TreeGraphEdge childEdge in Edges)
{
if (childEdge.IsTreeEdge)
{
yield return childEdge;
}
}
} }
} }
@ -64,28 +70,8 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{ {
get get
{ {
foreach (TreeEdge childEdge in ChildEdges) foreach (PositionedEdge outEdge in this.ChildEdges)
yield return (TreeNode)childEdge.TargetNode; yield return (TreeNode)outEdge.Target;
}
}
private List<TreeEdge> additionalNeighbors = new List<TreeEdge>();
public List<TreeEdge> AdditionalNeighbors
{
get
{
return additionalNeighbors;
}
}
public override IEnumerable<PositionedEdge> Edges
{
get
{
foreach (TreeEdge childEdge in ChildEdges)
yield return childEdge;
foreach (TreeEdge neighborEdge in AdditionalNeighbors)
yield return neighborEdge;
} }
} }
} }

2
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs

@ -14,7 +14,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary> /// </summary>
public class TreeNodeLR: TreeNode public class TreeNodeLR: TreeNode
{ {
public TreeNodeLR(NodeControl nodeControl, ObjectNode objectNode) : base(nodeControl, objectNode) public TreeNodeLR(ObjectNode objectNode) : base(objectNode)
{ {
} }

2
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs

@ -14,7 +14,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary> /// </summary>
public class TreeNodeTB : TreeNode public class TreeNodeTB : TreeNode
{ {
public TreeNodeTB(NodeControl nodeControl, ObjectNode objectNode) : base(nodeControl, objectNode) public TreeNodeTB(ObjectNode objectNode) : base(objectNode)
{ {
} }

4
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/NamedEdge.cs

@ -28,11 +28,11 @@ namespace Debugger.AddIn.Visualizers.Graph
/// <summary> /// <summary>
/// Target node of the edge. /// Target node of the edge.
/// </summary> /// </summary>
public TTarget TargetNode { get; set; } public TTarget Target { get; set; }
/// <summary> /// <summary>
/// Source node of the edge. /// Source node of the edge.
/// </summary> /// </summary>
public TSource SourceNode { get; set; } public TSource Source { get; set; }
} }
} }

5
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraph.cs

@ -37,11 +37,12 @@ namespace Debugger.AddIn.Visualizers.Graph
{ {
get { return _nodes; } get { return _nodes; }
} }
/*
/// <summary> /// <summary>
/// All edges in the graph. /// All edges in the graph.
/// </summary> /// </summary>
/*public IEnumerable<ObjectEdge> Edges public IEnumerable<ObjectEdge> Edges
{ {
get get
{ {

133
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs

@ -43,10 +43,13 @@ namespace Debugger.AddIn.Visualizers.Graph
/// The underlying debugger service used for getting expression values. /// The underlying debugger service used for getting expression values.
/// </summary> /// </summary>
private WindowsDebugger debuggerService; private WindowsDebugger debuggerService;
private ObjectGraph resultGraph;
/// <summary> /// <summary>
/// The resulting object graph. /// Underlying object graph data struture.
/// </summary> /// </summary>
private ObjectGraph resultGraph; public ObjectGraph ResultGraph { get { return this.resultGraph; } }
/// <summary> /// <summary>
/// System.Runtime.CompilerServices.GetHashCode method, for obtaining non-overriden hash codes from debuggee. /// System.Runtime.CompilerServices.GetHashCode method, for obtaining non-overriden hash codes from debuggee.
/// </summary> /// </summary>
@ -60,7 +63,7 @@ namespace Debugger.AddIn.Visualizers.Graph
/// <summary> /// <summary>
/// Binding flags for getting member expressions. /// Binding flags for getting member expressions.
/// </summary> /// </summary>
private readonly Debugger.MetaData.BindingFlags _bindingFlags = private readonly Debugger.MetaData.BindingFlags _bindingFlags =
BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty; BindingFlags.Public | BindingFlags.Instance | BindingFlags.Field | BindingFlags.GetProperty;
/// <summary> /// <summary>
@ -80,7 +83,7 @@ namespace Debugger.AddIn.Visualizers.Graph
/// </summary> /// </summary>
/// <param name="expression">Expression valid in the program being debugged (eg. variable name)</param> /// <param name="expression">Expression valid in the program being debugged (eg. variable name)</param>
/// <returns>Object graph</returns> /// <returns>Object graph</returns>
public ObjectGraph BuildGraphForExpression(string expression) public ObjectGraph BuildGraphForExpression(string expression, ExpandedNodes expandedNodes)
{ {
if (string.IsNullOrEmpty(expression)) if (string.IsNullOrEmpty(expression))
{ {
@ -92,37 +95,52 @@ namespace Debugger.AddIn.Visualizers.Graph
// empty graph for null expression // empty graph for null expression
if (!debuggerService.GetValueFromName(expression).IsNull) if (!debuggerService.GetValueFromName(expression).IsNull)
{ {
resultGraph.Root = buildGraphRecursive(debuggerService.GetValueFromName(expression).GetPermanentReference()); //resultGraph.Root = buildGraphRecursive(debuggerService.GetValueFromName(expression).GetPermanentReference(), expandedNodes);
resultGraph.Root = createNewNode(debuggerService.GetValueFromName(expression).GetPermanentReference());
loadNodeProperties(resultGraph.Root);
loadChildrenRecursive(resultGraph.Root, expandedNodes);
} }
return resultGraph; return resultGraph;
} }
public ObjectNode ObtainNodeForExpression(Expression expr, out bool createdNewNode)
{
return ObtainNodeForValue(getPermanentReference(expr), out createdNewNode);
}
public ObjectNode ObtainNodeForExpression(Expression expr)
{
bool createdNewNode; // ignore (caller is not interested, otherwise he would use the other overload)
return ObtainNodeForExpression(expr, out createdNewNode);
}
/// <summary> /// <summary>
/// Builds the subgraph representing given value. /// Returns node in the graph that represents given value, or returns new node if no node found.
/// </summary> /// </summary>
/// <param name="rootValue">The Value for which the subgraph will be built.</param> /// <param name="value">Value for which to obtain the node/</param>
/// <returns>ObjectNode representing the value + all recursive members.</returns> /// <param name="createdNew">True if new node was created, false if existing node was returned.</param>
private ObjectNode buildGraphRecursive(Value rootValue) public ObjectNode ObtainNodeForValue(Value value, out bool createdNew)
{ {
ObjectNode thisNode = createNewNode(rootValue); createdNew = false;
ObjectNode nodeForValue = getExistingNodeForValue(value);
// David: by calling this, we get an array of values, most of them probably invalid, if (nodeForValue == null)
// it would be nice to be able to get a collection of 'values'
// that are valid (basically a snapshot of object's state)
// - a collection of custom objects,
// that contain the string value and DebugType,
// and can enumerate child values.
// It would be also nice to return IEnumerable or ReadonlyCollection
// http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-considered-somewhat-harmful.aspx
/*string[] memberValues = rootValue.GetMemberValuesAsString(_bindingFlags);
foreach (string memberValue in memberValues)
{ {
//Value memberValuePerm = memberValue.GetPermanentReference(); // if no node for memberValue exists, create it
nodeForValue = createNewNode(value);
}*/ loadNodeProperties(nodeForValue);
createdNew = true;
foreach(Expression memberExpr in rootValue.Expression.AppendObjectMembers(rootValue.Type, _bindingFlags)) }
return nodeForValue;
}
/// <summary>
/// Fills node contents by adding properties.
/// </summary>
/// <param name="thisNode"></param>
private void loadNodeProperties(ObjectNode thisNode)
{
foreach(Expression memberExpr in thisNode.DebuggerValue.Expression.AppendObjectMembers(thisNode.DebuggerValue.Type, _bindingFlags))
{ {
checkIsOfSupportedType(memberExpr); checkIsOfSupportedType(memberExpr);
@ -131,33 +149,48 @@ namespace Debugger.AddIn.Visualizers.Graph
{ {
// atomic members are added to the list of node's "properties" // atomic members are added to the list of node's "properties"
string memberValueAsString = memberExpr.Evaluate(debuggerService.DebuggedProcess).AsString; string memberValueAsString = memberExpr.Evaluate(debuggerService.DebuggedProcess).AsString;
thisNode.AddAtomicProperty(memberName, memberValueAsString, memberExpr); thisNode.AddAtomicProperty(memberName, memberValueAsString, memberExpr);
} }
else else
{ {
// for object members, complex properties are added
ObjectNode targetNode = null; ObjectNode targetNode = null;
if (!isNull(memberExpr)) bool memberIsNull = isNull(memberExpr);
{ thisNode.AddComplexProperty(memberName, "", memberExpr, targetNode, memberIsNull);
// for object members, edges are added }
Value memberValue = getPermanentReference(memberExpr); }
}
// if node for memberValue already exists, only add edge to it (so loops etc. are solved correctly)
targetNode = getNodeForValue(memberValue); /// <summary>
if (targetNode == null) /// Creates child nodes of this node for each complex property and connects them to property.TargetNode.
{ /// </summary>
// if no node for memberValue exists, build the subgraph for the value /// <param name="thisNode"></param>
targetNode = buildGraphRecursive(memberValue); /// <param name="expandedNodes"></param>
} private void loadChildrenRecursive(ObjectNode thisNode, ExpandedNodes expandedNodes)
} {
else foreach(ObjectProperty complexProperty in thisNode.ComplexProperties)
{
Expression memberExpr = complexProperty.Expression;
ObjectNode targetNode = null;
if (!complexProperty.IsNull && expandedNodes.IsExpanded(memberExpr.Code))
{
Value memberValue = getPermanentReference(memberExpr);
bool createdNew;
// get existing node (loop) or create new
targetNode = ObtainNodeForValue(memberValue, out createdNew);
if (createdNew)
{ {
targetNode = null; // if member node is new, recursively build its subtree
loadChildrenRecursive(targetNode, expandedNodes);
} }
thisNode.AddComplexProperty(memberName, "", memberExpr, targetNode);
} }
else
{
targetNode = null;
}
complexProperty.TargetNode = targetNode;
} }
return thisNode;
} }
/// <summary> /// <summary>
@ -174,9 +207,9 @@ namespace Debugger.AddIn.Visualizers.Graph
// remember this node's hashcode for quick lookup // remember this node's hashcode for quick lookup
objectNodesForHashCode.Add(newNode.HashCode, newNode); objectNodesForHashCode.Add(newNode.HashCode, newNode);
// permanent reference to the object this node represents is useful for graph building, // permanent reference to the object this node represents is useful for graph building,
// and matching nodes in animations // and matching nodes in animations
newNode.PermanentReference = permanentReference; newNode.DebuggerValue = permanentReference;
return newNode; return newNode;
} }
@ -186,7 +219,7 @@ namespace Debugger.AddIn.Visualizers.Graph
/// </summary> /// </summary>
/// <param name="value">Valid value representing an instance.</param> /// <param name="value">Valid value representing an instance.</param>
/// <returns></returns> /// <returns></returns>
private ObjectNode getNodeForValue(Value value) private ObjectNode getExistingNodeForValue(Value value)
{ {
int objectHashCode = invokeGetHashCode(value); int objectHashCode = invokeGetHashCode(value);
// are there any nodes with the same hash code? // are there any nodes with the same hash code?
@ -201,7 +234,7 @@ namespace Debugger.AddIn.Visualizers.Graph
// (hash codes are not uniqe - http://stackoverflow.com/questions/750947/-net-unique-object-identifier) // (hash codes are not uniqe - http://stackoverflow.com/questions/750947/-net-unique-object-identifier)
ulong objectAddress = value.GetObjectAddress(); ulong objectAddress = value.GetObjectAddress();
ObjectNode nodeWithSameAddress = nodesWithSameHashCode.Find( ObjectNode nodeWithSameAddress = nodesWithSameHashCode.Find(
node => { return objectAddress == node.PermanentReference.GetObjectAddress(); } ); node => { return node.DebuggerValue.GetObjectAddress() == objectAddress; } );
return nodeWithSameAddress; return nodeWithSameAddress;
} }
} }
@ -250,7 +283,7 @@ namespace Debugger.AddIn.Visualizers.Graph
} }
#region Expression helpers #region Expression helpers
private Value getPermanentReference(Expression expr) private Value getPermanentReference(Expression expr)
{ {
return expr.Evaluate(debuggerService.DebuggedProcess).GetPermanentReference(); return expr.Evaluate(debuggerService.DebuggedProcess).GetPermanentReference();

36
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectNode.cs

@ -17,10 +17,17 @@ namespace Debugger.AddIn.Visualizers.Graph
public class ObjectNode public class ObjectNode
{ {
/// <summary> /// <summary>
/// Additional info useful for internal algorithms, not to be visible to the user. /// Permanent reference to the value in the the debugee this node represents.
/// </summary>
internal Debugger.Value DebuggerValue { get; set; }
/// <summary>
/// Hash code in the debuggee of the DebuggerValue this node represents.
/// </summary> /// </summary>
internal Debugger.Value PermanentReference { get; set; }
internal int HashCode { get; set; } internal int HashCode { get; set; }
/// <summary>
/// Expression used to obtain this node.
/// </summary>
public Expressions.Expression Expression { get { return this.DebuggerValue.Expression; } }
/*private List<ObjectEdge> _edges = new List<ObjectEdge>(); /*private List<ObjectEdge> _edges = new List<ObjectEdge>();
/// <summary> /// <summary>
@ -43,7 +50,16 @@ namespace Debugger.AddIn.Visualizers.Graph
{ {
public int Compare(ObjectProperty prop1, ObjectProperty prop2) public int Compare(ObjectProperty prop1, ObjectProperty prop2)
{ {
return prop1.Name.CompareTo(prop2.Name); // order by IsAtomic, Name
int atomic = prop2.IsAtomic.CompareTo(prop1.IsAtomic);
if (atomic != 0)
{
return atomic;
}
else
{
return prop1.Name.CompareTo(prop2.Name);
}
} }
} }
@ -51,7 +67,7 @@ namespace Debugger.AddIn.Visualizers.Graph
private bool sorted = false; private bool sorted = false;
private List<ObjectProperty> _properties = new List<ObjectProperty>(); private List<ObjectProperty> properties = new List<ObjectProperty>();
/// <summary> /// <summary>
/// Properties (either atomic or complex) of the object this node represents. /// Properties (either atomic or complex) of the object this node represents.
/// </summary> /// </summary>
@ -61,10 +77,10 @@ namespace Debugger.AddIn.Visualizers.Graph
{ {
if (!sorted) if (!sorted)
{ {
_properties.Sort(propertySortComparer); properties.Sort(propertySortComparer);
sorted = true; sorted = true;
} }
return _properties; return properties;
} }
} }
/// <summary> /// <summary>
@ -87,17 +103,17 @@ namespace Debugger.AddIn.Visualizers.Graph
/// </summary> /// </summary>
internal void AddAtomicProperty(string name, string value, Expression expression) internal void AddAtomicProperty(string name, string value, Expression expression)
{ {
_properties.Add(new ObjectProperty properties.Add(new ObjectProperty
{ Name = name, Value = value, Expression = expression, IsAtomic = true, TargetNode = null }); { Name = name, Value = value, Expression = expression, IsAtomic = true, TargetNode = null });
} }
/// <summary> /// <summary>
/// Adds complex property. /// Adds complex property.
/// </summary> /// </summary>
internal void AddComplexProperty(string name, string value, Expression expression, ObjectNode targetNode) internal void AddComplexProperty(string name, string value, Expression expression, ObjectNode targetNode, bool isNull)
{ {
_properties.Add(new ObjectProperty properties.Add(new ObjectProperty
{ Name = name, Value = value, Expression = expression, IsAtomic = false, TargetNode = targetNode }); { Name = name, Value = value, Expression = expression, IsAtomic = false, TargetNode = targetNode, IsNull = isNull });
} }
} }
} }

13
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectProperty.cs

@ -19,21 +19,30 @@ namespace Debugger.AddIn.Visualizers.Graph
/// e.g. "Age" /// e.g. "Age"
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// e.g. "19" /// e.g. "19"
/// </summary> /// </summary>
public string Value { get; set; } public string Value { get; set; }
/// <summary> /// <summary>
/// Expression used for obtaining this property /// Expression used for obtaining this property
/// </summary> /// </summary>
public Debugger.Expressions.Expression Expression { get; set; } public Debugger.Expressions.Expression Expression { get; set; }
/// <summary>
/// Is this property of atomic type? (int, string, etc.)
/// </summary>
public bool IsAtomic { get; set; }
/// <summary> /// <summary>
/// Node that this property points to. Can be null. Always null if <see cref="IsAtomic"/> is true. /// Node that this property points to. Can be null. Always null if <see cref="IsAtomic"/> is true.
/// </summary> /// </summary>
public ObjectNode TargetNode { get; set; } public ObjectNode TargetNode { get; set; }
/// <summary> /// <summary>
/// Is this property of atomic type? (int, string, etc.) /// Is this property value null? Only meaningful if <see cref="IsAtomic"/> is false.
/// </summary> /// </summary>
public bool IsAtomic { get; set; } public bool IsNull { get; set; }
} }
} }

39
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraphVisualizerViewContent.cs

@ -1,39 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// ViewContent of the visualizer.
/// </summary>
public class ObjectGraphVisualizerViewContent : AbstractViewContent
{
VisualizerWinFormsControl control = new VisualizerWinFormsControl();
/// <summary>
/// The <see cref="System.Windows.Forms.Control"/> representing the view.
/// </summary>
public override object Control
{
get
{
return control;
}
}
public ObjectGraphVisualizerViewContent()
{
this.TitleName = "Object graph visualizer";
}
}
}

12
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml

@ -1,12 +0,0 @@
<UserControl x:Class="Debugger.AddIn.Visualizers.Graph.VisualizerWPFControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="300" Width="300">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Padding="4">Expression:</TextBlock>
<TextBox VerticalAlignment="Center" Margin="4" Width="100"></TextBox>
<Button Padding="4" Margin="4">Inspect</Button>
</StackPanel>
<Canvas></Canvas>
</StackPanel>
</UserControl>

33
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFControl.xaml.cs

@ -1,33 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Interaction logic for VisualizerWPFControl.xaml
/// </summary>
public partial class VisualizerWPFControl : UserControl
{
public VisualizerWPFControl()
{
InitializeComponent();
}
}
}

75
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs

@ -31,10 +31,16 @@ namespace Debugger.AddIn.Visualizers.Graph
private WindowsDebugger debuggerService; private WindowsDebugger debuggerService;
private EnumViewModel<LayoutDirection> layoutViewModel; private EnumViewModel<LayoutDirection> layoutViewModel;
private ObjectGraph objectGraph; private ObjectGraph objectGraph;
private ObjectGraphBuilder objectGraphBuilder;
private PositionedGraph oldGraph; private PositionedGraph oldPosGraph;
private PositionedGraph currentGraph; private PositionedGraph currentPosGraph;
private GraphDrawer graphDrawer; private GraphDrawer graphDrawer;
/// <summary>
/// Long-lived map telling which nodes the user expanded.
/// </summary>
private ExpandedNodes expandedNodes;
public VisualizerWPFWindow() public VisualizerWPFWindow()
{ {
@ -52,6 +58,8 @@ namespace Debugger.AddIn.Visualizers.Graph
this.DataContext = this.layoutViewModel; this.DataContext = this.layoutViewModel;
this.graphDrawer = new GraphDrawer(this.canvas); this.graphDrawer = new GraphDrawer(this.canvas);
this.expandedNodes = new ExpandedNodes();
} }
public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e) public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e)
@ -84,41 +92,43 @@ namespace Debugger.AddIn.Visualizers.Graph
void refreshGraph() void refreshGraph()
{ {
ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(debuggerService); this.objectGraph = rebuildGraph();
this.objectGraph = null; layoutGraph(this.objectGraph);
//GraphDrawer drawer = new GraphDrawer(graph);
//drawer.Draw(canvas);
}
ObjectGraph rebuildGraph()
{
this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService);
try try
{ {
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text); ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text);
this.objectGraph = graphBuilder.BuildGraphForExpression(txtExpression.Text); return this.objectGraphBuilder.BuildGraphForExpression(txtExpression.Text, this.expandedNodes);
} }
catch(DebuggerVisualizerException ex) catch(DebuggerVisualizerException ex)
{ {
guiHandleException(ex); guiHandleException(ex);
return; return null;
} }
catch(Debugger.GetValueException ex) catch(Debugger.GetValueException ex)
{ {
guiHandleException(ex); guiHandleException(ex);
return; return null;
} }
}
layoutGraph(this.objectGraph);
//GraphDrawer drawer = new GraphDrawer(graph);
//drawer.Draw(canvas);
}
void layoutGraph(ObjectGraph graph) void layoutGraph(ObjectGraph graph)
{ {
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout"); ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout");
Layout.TreeLayouter layouter = new Layout.TreeLayouter(); Layout.TreeLayouter layouter = new Layout.TreeLayouter();
this.oldGraph = this.currentGraph; this.oldPosGraph = this.currentPosGraph;
this.currentGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue); this.currentPosGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expandedNodes);
registerExpandCollapseEvents(this.currentPosGraph);
var graphDiff = new GraphMatcher().MatchGraphs(oldGraph, currentGraph); var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph);
this.graphDrawer.StartAnimation(oldGraph, currentGraph, graphDiff); this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff);
//this.graphDrawer.Draw(currentGraph); //this.graphDrawer.Draw(currentGraph);
} }
@ -126,5 +136,34 @@ namespace Debugger.AddIn.Visualizers.Graph
{ {
MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
} }
void registerExpandCollapseEvents(PositionedGraph posGraph)
{
foreach (var node in posGraph.Nodes)
{
node.Expanded += new EventHandler<PositionedPropertyEventArgs>(node_Expanded);
node.Collapsed += new EventHandler<PositionedPropertyEventArgs>(node_Collapsed);
}
}
void node_Expanded(object sender, PositionedPropertyEventArgs e)
{
// remember this property is expanded (for later graph rebuilds)
expandedNodes.SetExpanded(e.Property.Expression.Code);
// add edge (+ possibly node) to underlying object graph (no need to rebuild)
e.Property.ObjectProperty.TargetNode = this.objectGraphBuilder.ObtainNodeForExpression(e.Property.Expression);
layoutGraph(this.objectGraph);
}
void node_Collapsed(object sender, PositionedPropertyEventArgs e)
{
// remember this property is collapsed (for later graph rebuilds)
expandedNodes.SetCollapsed(e.Property.Expression.Code);
// just remove edge from underlying object graph (no need to rebuild)
e.Property.ObjectProperty.TargetNode = null;
layoutGraph(this.objectGraph);
}
} }
} }

92
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.Designer.cs generated

@ -1,92 +0,0 @@
namespace Debugger.AddIn.Visualizers.Graph
{
partial class VisualizerWinFormsControl
{
/// <summary>
/// Designer variable used to keep track of non-visual components.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Disposes resources used by the control.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing) {
if (components != null) {
components.Dispose();
}
}
base.Dispose(disposing);
}
/// <summary>
/// This method is required for Windows Forms designer support.
/// Do not change the method contents inside the source code editor. The Forms designer might
/// not be able to load this method if it was changed manually.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.txtExpression = new System.Windows.Forms.TextBox();
this.btnInspect = new System.Windows.Forms.Button();
this.lblInfo = new System.Windows.Forms.Label();
this.lblExpression = new System.Windows.Forms.Label();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.SuspendLayout();
//
// txtExpression
//
this.txtExpression.Location = new System.Drawing.Point(88, 13);
this.txtExpression.Name = "txtExpression";
this.txtExpression.Size = new System.Drawing.Size(131, 20);
this.txtExpression.TabIndex = 0;
this.toolTip1.SetToolTip(this.txtExpression, "Expression (e.g. variable name) to be inspected");
//
// btnInspect
//
this.btnInspect.Location = new System.Drawing.Point(225, 10);
this.btnInspect.Name = "btnInspect";
this.btnInspect.Size = new System.Drawing.Size(75, 23);
this.btnInspect.TabIndex = 1;
this.btnInspect.Text = "Inspect";
this.btnInspect.UseVisualStyleBackColor = true;
this.btnInspect.Click += new System.EventHandler(this.BtnInspectClick);
//
// lblInfo
//
this.lblInfo.Location = new System.Drawing.Point(11, 58);
this.lblInfo.Name = "lblInfo";
this.lblInfo.Size = new System.Drawing.Size(349, 23);
this.lblInfo.TabIndex = 2;
this.lblInfo.Text = "< Result >";
//
// lblExpression
//
this.lblExpression.Location = new System.Drawing.Point(11, 14);
this.lblExpression.Name = "lblExpression";
this.lblExpression.Size = new System.Drawing.Size(71, 23);
this.lblExpression.TabIndex = 4;
this.lblExpression.Text = "Expression:";
//
// VisualizerWinFormsControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.lblExpression);
this.Controls.Add(this.lblInfo);
this.Controls.Add(this.btnInspect);
this.Controls.Add(this.txtExpression);
this.Name = "VisualizerWinFormsControl";
this.Size = new System.Drawing.Size(525, 426);
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.Button btnInspect;
private System.Windows.Forms.ToolTip toolTip1;
private System.Windows.Forms.Label lblExpression;
private System.Windows.Forms.Label lblInfo;
private System.Windows.Forms.TextBox txtExpression;
}
}

84
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWinFormsControl.cs

@ -1,84 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníèek" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Linq;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Services;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Windows forms control making up the object graph visualizer user interface/
/// </summary>
public partial class VisualizerWinFormsControl : UserControl
{
WindowsDebugger _debuggerService = null;
public VisualizerWinFormsControl()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
_debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger;
if (_debuggerService == null)
throw new ApplicationException("Only windows debugger is currently supported");
_debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged);
}
public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e)
{
// on step, breakpoint hit
if (!_debuggerService.IsProcessRunning)
{
refreshGraph();
}
}
void BtnInspectClick(object sender, EventArgs e)
{
refreshGraph();
}
void refreshGraph()
{
ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(_debuggerService);
ObjectGraph graph = null;
try
{
graph = graphBuilder.BuildGraphForExpression(txtExpression.Text);
}
catch(DebuggerVisualizerException ex)
{
guiHandleException(ex);
return;
}
catch(Debugger.GetValueException ex)
{
guiHandleException(ex);
return;
}
// just a simple message for checking the graph is build ok, will be replaced by graph drawing of course
//lblInfo.Text = string.Format("Done. Number of graph nodes: {0}, number of edges: {1}", graph.Nodes.Count(), graph.Edges.Count());
}
void guiHandleException(System.Exception ex)
{
lblInfo.Text = "< Result >";
MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}

4
src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/TestPrograms/MemoryReadWrite.cs

@ -75,8 +75,8 @@ namespace Debugger.Tests {
<ModuleLoaded>MemoryReadWrite.exe (Has symbols)</ModuleLoaded> <ModuleLoaded>MemoryReadWrite.exe (Has symbols)</ModuleLoaded>
<ModuleLoaded>System.dll (No symbols)</ModuleLoaded> <ModuleLoaded>System.dll (No symbols)</ModuleLoaded>
<DebuggingPaused>Break MemoryReadWrite.cs:18,4-18,40</DebuggingPaused> <DebuggingPaused>Break MemoryReadWrite.cs:18,4-18,40</DebuggingPaused>
<hello>0 A 33 79 6 0 0 0 5 0 0 0 48 0 65 0 6C 0 6C 0 6F 0 </hello> <hello>0 A 4D 3 6 0 0 0 5 0 0 0 48 0 65 0 6C 0 6C 0 6F 0 </hello>
<world>0 A 33 79 7 0 0 0 6 0 0 0 20 0 20 0 20 0 20 0 20 0 21 0 </world> <world>0 A 4D 3 7 0 0 0 6 0 0 0 20 0 20 0 20 0 20 0 20 0 21 0 </world>
<ModuleLoaded>System.Configuration.dll (No symbols)</ModuleLoaded> <ModuleLoaded>System.Configuration.dll (No symbols)</ModuleLoaded>
<ModuleLoaded>System.Xml.dll (No symbols)</ModuleLoaded> <ModuleLoaded>System.Xml.dll (No symbols)</ModuleLoaded>
<LogMessage>Hello world!\r\n</LogMessage> <LogMessage>Hello world!\r\n</LogMessage>

6
src/AddIns/Misc/UnitTesting/Test/Utils/MockProjectContent.cs

@ -180,5 +180,11 @@ namespace UnitTesting.Tests.Utils
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public string AssemblyName {
get {
throw new NotImplementedException();
}
}
} }
} }

1
src/Libraries/AvalonDock/AvalonDock.csproj

@ -76,6 +76,7 @@
<Compile Include="AlignedImage.cs" /> <Compile Include="AlignedImage.cs" />
<Compile Include="AvalonDockBrushes.cs" /> <Compile Include="AvalonDockBrushes.cs" />
<Compile Include="ContextMenuElement.cs" /> <Compile Include="ContextMenuElement.cs" />
<Compile Include="DeserializationCallbackEventArgs.cs" />
<Compile Include="DockableContent.cs" /> <Compile Include="DockableContent.cs" />
<Compile Include="DockableFloatingWindow.cs" /> <Compile Include="DockableFloatingWindow.cs" />
<Compile Include="DockablePane.cs" /> <Compile Include="DockablePane.cs" />

25
src/Libraries/AvalonDock/DeserializationCallbackEventArgs.cs

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AvalonDock
{
public class DeserializationCallbackEventArgs : EventArgs
{
public DeserializationCallbackEventArgs(string contentName)
{
Name = contentName;
}
/// <summary>
/// Gets the name of the content to deserialize
/// </summary>
public string Name { get; protected set; }
/// <summary>
/// Gets/Sets the content manually deserialized
/// </summary>
public DockableContent Content { get; set; }
}
}

8
src/Libraries/AvalonDock/DockableContent.cs

@ -156,6 +156,8 @@ namespace AvalonDock
public readonly double Width; public readonly double Width;
public readonly double Height; public readonly double Height;
public readonly AnchorStyle Anchor = AnchorStyle.None;
public DockableContentStateAndPosition( public DockableContentStateAndPosition(
DockableContent cntToSave) DockableContent cntToSave)
{ {
@ -163,6 +165,12 @@ namespace AvalonDock
ChildIndex = ContainerPane.Items.IndexOf(cntToSave); ChildIndex = ContainerPane.Items.IndexOf(cntToSave);
Width = ContainerPane.ActualWidth; Width = ContainerPane.ActualWidth;
Height = ContainerPane.ActualHeight; Height = ContainerPane.ActualHeight;
DockablePane dockablePane = ContainerPane as DockablePane;
if (dockablePane != null)
{
Anchor = dockablePane.Anchor;
}
} }
} }

6
src/Libraries/AvalonDock/DockablePane.cs

@ -47,6 +47,10 @@ namespace AvalonDock
/// </summary> /// </summary>
public enum AnchorStyle public enum AnchorStyle
{ {
/// <summary>
/// No anchor style, while content is hosted in a <see cref="DocumentPane"/> or a <see cref="FloatingWindow"/>
/// </summary>
None,
/// <summary> /// <summary>
/// Top border anchor /// Top border anchor
/// </summary> /// </summary>
@ -135,7 +139,7 @@ namespace AvalonDock
// Using a DependencyProperty as the backing store for Anchor. This enables animation, styling, binding, etc... // Using a DependencyProperty as the backing store for Anchor. This enables animation, styling, binding, etc...
public static readonly DependencyPropertyKey AnchorPropertyKey = public static readonly DependencyPropertyKey AnchorPropertyKey =
DependencyProperty.RegisterAttachedReadOnly("Anchor", typeof(AnchorStyle), typeof(DockablePane), new UIPropertyMetadata(AnchorStyle.Left)); DependencyProperty.RegisterAttachedReadOnly("Anchor", typeof(AnchorStyle), typeof(DockablePane), new UIPropertyMetadata(AnchorStyle.None));
public override void OnApplyTemplate() public override void OnApplyTemplate()

140
src/Libraries/AvalonDock/DockingManager.cs

@ -213,8 +213,8 @@ namespace AvalonDock
} }
set set
{ {
if (_activeDocument != value && if (_activeDocument != value/* &&
value.ContainerPane is DocumentPane) value.ContainerPane is DocumentPane*/)
{ {
List<ManagedContent> listOfAllDocuments = FindContents<ManagedContent>(); List<ManagedContent> listOfAllDocuments = FindContents<ManagedContent>();
listOfAllDocuments.ForEach((ManagedContent cnt) => listOfAllDocuments.ForEach((ManagedContent cnt) =>
@ -685,6 +685,9 @@ namespace AvalonDock
//remove the pane from its original children collection //remove the pane from its original children collection
FrameworkElement parentElement = paneToAnchor.Parent as FrameworkElement; FrameworkElement parentElement = paneToAnchor.Parent as FrameworkElement;
if (anchor == AnchorStyle.None)
anchor = AnchorStyle.Right;
//Change anchor border according to FlowDirection //Change anchor border according to FlowDirection
if (FlowDirection == FlowDirection.RightToLeft) if (FlowDirection == FlowDirection.RightToLeft)
{ {
@ -801,6 +804,9 @@ namespace AvalonDock
/// <param name="anchor"></param> /// <param name="anchor"></param>
public void Anchor(Pane paneToAnchor, Pane relativePane, AnchorStyle anchor) public void Anchor(Pane paneToAnchor, Pane relativePane, AnchorStyle anchor)
{ {
if (anchor == AnchorStyle.None)
anchor = AnchorStyle.Right;
//Change anchor border according to FlowDirection //Change anchor border according to FlowDirection
if (FlowDirection == FlowDirection.RightToLeft) if (FlowDirection == FlowDirection.RightToLeft)
{ {
@ -832,6 +838,9 @@ namespace AvalonDock
/// <param name="anchor"></param> /// <param name="anchor"></param>
public void Anchor(DockablePane paneToAnchor, DocumentPane relativePane, AnchorStyle anchor) public void Anchor(DockablePane paneToAnchor, DocumentPane relativePane, AnchorStyle anchor)
{ {
if (anchor == AnchorStyle.None)
anchor = AnchorStyle.Right;
//get a reference to the resizingpanel container of relativePane //get a reference to the resizingpanel container of relativePane
ResizingPanel relativePaneContainer = LogicalTreeHelper.GetParent(relativePane) as ResizingPanel; ResizingPanel relativePaneContainer = LogicalTreeHelper.GetParent(relativePane) as ResizingPanel;
DocumentPaneResizingPanel relativeDocumentPaneContainer = relativePane.GetParentDocumentPaneResizingPanel(); DocumentPaneResizingPanel relativeDocumentPaneContainer = relativePane.GetParentDocumentPaneResizingPanel();
@ -981,6 +990,9 @@ namespace AvalonDock
/// <param name="anchor"></param> /// <param name="anchor"></param>
public void Anchor(DocumentPane paneToAnchor, DocumentPane relativePane, AnchorStyle anchor) public void Anchor(DocumentPane paneToAnchor, DocumentPane relativePane, AnchorStyle anchor)
{ {
if (anchor == AnchorStyle.None)
anchor = AnchorStyle.Right;
//get a reference to the resizingpanel container of relativePane //get a reference to the resizingpanel container of relativePane
ResizingPanel relativePaneContainer = LogicalTreeHelper.GetParent(relativePane) as ResizingPanel; ResizingPanel relativePaneContainer = LogicalTreeHelper.GetParent(relativePane) as ResizingPanel;
DocumentPaneResizingPanel relativeDocumentPaneContainer = relativePane.GetParentDocumentPaneResizingPanel(); DocumentPaneResizingPanel relativeDocumentPaneContainer = relativePane.GetParentDocumentPaneResizingPanel();
@ -1083,6 +1095,9 @@ namespace AvalonDock
/// <param name="anchor"></param> /// <param name="anchor"></param>
public void Anchor(DockablePane paneToAnchor, DockablePane relativePane, AnchorStyle anchor) public void Anchor(DockablePane paneToAnchor, DockablePane relativePane, AnchorStyle anchor)
{ {
if (anchor == AnchorStyle.None)
anchor = AnchorStyle.Right;
//get a refernce to the resizingpanel container of relativePane //get a refernce to the resizingpanel container of relativePane
ResizingPanel relativePaneContainer = LogicalTreeHelper.GetParent(relativePane) as ResizingPanel; ResizingPanel relativePaneContainer = LogicalTreeHelper.GetParent(relativePane) as ResizingPanel;
Orientation requestedOrientation = Orientation requestedOrientation =
@ -1536,7 +1551,7 @@ namespace AvalonDock
/// <param name="desideredState">State desidered</param> /// <param name="desideredState">State desidered</param>
public void Show(DockableContent content, DockableContentState desideredState) public void Show(DockableContent content, DockableContentState desideredState)
{ {
Show(content, desideredState, AnchorStyle.Right); Show(content, desideredState, AnchorStyle.None);
} }
/// <summary> /// <summary>
@ -1696,6 +1711,14 @@ namespace AvalonDock
DockablePane newHostpane = new DockablePane(); DockablePane newHostpane = new DockablePane();
newHostpane.Items.Add(content); newHostpane.Items.Add(content);
if (desideredAnchor == AnchorStyle.None &&
content.SavedStateAndPosition != null &&
content.SavedStateAndPosition.Anchor != AnchorStyle.None)
desideredAnchor = content.SavedStateAndPosition.Anchor;
if (desideredAnchor == AnchorStyle.None)
desideredAnchor = AnchorStyle.Right;
if (desideredAnchor == AnchorStyle.Left || if (desideredAnchor == AnchorStyle.Left ||
desideredAnchor == AnchorStyle.Right) desideredAnchor == AnchorStyle.Right)
{ {
@ -1705,7 +1728,8 @@ namespace AvalonDock
!double.IsNaN(content.SavedStateAndPosition.Width)) !double.IsNaN(content.SavedStateAndPosition.Width))
w = content.SavedStateAndPosition.Width; w = content.SavedStateAndPosition.Width;
//ResizingPanel.SetResizeWidth(newHostpane, w); ResizingPanel.SetResizeWidth(newHostpane, new GridLength(w));
ResizingPanel.SetEffectiveSize(newHostpane, new Size(w, 0.0));
} }
else else
{ {
@ -1715,7 +1739,8 @@ namespace AvalonDock
!double.IsNaN(content.SavedStateAndPosition.Height)) !double.IsNaN(content.SavedStateAndPosition.Height))
h = content.SavedStateAndPosition.Height; h = content.SavedStateAndPosition.Height;
//ResizingPanel.SetResizeHeight(newHostpane, h); ResizingPanel.SetResizeHeight(newHostpane, new GridLength(h));
ResizingPanel.SetEffectiveSize(newHostpane, new Size(0.0, h));
} }
Anchor(newHostpane, desideredAnchor); Anchor(newHostpane, desideredAnchor);
@ -2474,13 +2499,18 @@ namespace AvalonDock
{ {
if (content.State == DockableContentState.AutoHide) if (content.State == DockableContentState.AutoHide)
{ {
if ((content.Parent as DockablePane).Items.Count == 1) DockablePane parentContainer = content.Parent as DockablePane;
if (parentContainer != null &&
parentContainer.Items.Count == 1)
ToggleAutoHide(content.Parent as DockablePane); ToggleAutoHide(content.Parent as DockablePane);
} }
if (content.State == DockableContentState.DockableWindow || if (content.State == DockableContentState.DockableWindow ||
content.State == DockableContentState.FloatingWindow) content.State == DockableContentState.FloatingWindow)
{ {
if ((content.Parent as DockablePane).Items.Count == 1) DockablePane parentContainer = content.Parent as DockablePane;
if (parentContainer != null &&
parentContainer.Items.Count == 1)
{ {
FloatingWindow floatingWindow = Window.GetWindow(content) as FloatingWindow; FloatingWindow floatingWindow = Window.GetWindow(content) as FloatingWindow;
floatingWindow.Close(); floatingWindow.Close();
@ -2493,6 +2523,11 @@ namespace AvalonDock
content.DetachFromContainerPane(); content.DetachFromContainerPane();
} }
public delegate void DeserializationCallbackHandler(object sender, DeserializationCallbackEventArgs e);
public DeserializationCallbackHandler DeserializationCallback { get; set; }
void ShowAllHiddenContents() void ShowAllHiddenContents()
{ {
while (_hiddenContents.Count > 0) while (_hiddenContents.Count > 0)
@ -2502,7 +2537,7 @@ namespace AvalonDock
} }
} }
void RestoreDocumentPaneLayout(XmlElement childElement, out DocumentPane mainExistingDocumentPane, out DocumentPaneResizingPanel existingDocumentPanel) void RestoreDocumentPaneLayout(XmlElement childElement, out DocumentPane mainExistingDocumentPane, out DocumentPaneResizingPanel existingDocumentPanel, DockableContent[] dockableContents)
{ {
mainExistingDocumentPane = (Content is DocumentPane) ? Content as DocumentPane : GetMainDocumentPane(Content as ResizingPanel); mainExistingDocumentPane = (Content is DocumentPane) ? Content as DocumentPane : GetMainDocumentPane(Content as ResizingPanel);
existingDocumentPanel = mainExistingDocumentPane.GetParentDocumentPaneResizingPanel(); existingDocumentPanel = mainExistingDocumentPane.GetParentDocumentPaneResizingPanel();
@ -2534,17 +2569,36 @@ namespace AvalonDock
{ {
if (contentElement.HasAttribute("Name")) if (contentElement.HasAttribute("Name"))
{ {
foreach (DockableContent content in DockableContents) DockableContent foundContent = null;
string contentName = contentElement.GetAttribute("Name");
foreach (DockableContent content in dockableContents)
{ {
if (content.Name == contentElement.GetAttribute("Name")) if (content.Name == contentName)
{ {
DetachContentFromDockingManager(content); foundContent = content;
mainExistingDocumentPane.Items.Add(content);
content.SetStateToDocument();
content.RestoreLayout(contentElement);
break; break;
} }
} }
if (foundContent == null &&
DeserializationCallback != null)
{
DeserializationCallbackEventArgs e = new DeserializationCallbackEventArgs(contentName);
DeserializationCallback(this, e);
foundContent = e.Content;
}
if (foundContent != null)
{
DetachContentFromDockingManager(foundContent);
mainExistingDocumentPane.Items.Add(foundContent);
foundContent.SetStateToDocument();
//call custom layout persistence method
foundContent.RestoreLayout(contentElement);
}
} }
} }
} }
@ -2593,24 +2647,40 @@ namespace AvalonDock
{ {
if (contentElement.HasAttribute("Name")) if (contentElement.HasAttribute("Name"))
{ {
DockableContent foundContent = null;
string contentName = contentElement.GetAttribute("Name");
foreach (DockableContent content in dockableContents) foreach (DockableContent content in dockableContents)
{ {
if (content.Name == contentElement.GetAttribute("Name")) if (content.Name == contentName)
{ {
DetachContentFromDockingManager(content); foundContent = content;
pane.Items.Add(content);
content.SetStateToDock();
if (contentElement.HasAttribute("AutoHide") &&
XmlConvert.ToBoolean(contentElement.GetAttribute("AutoHide")) &&
pane.Items.Count == 1)
toggleAutoHide = true;
//call custom layout persistence method
content.RestoreLayout(contentElement);
break; break;
} }
} }
if (foundContent == null &&
DeserializationCallback != null)
{
DeserializationCallbackEventArgs e = new DeserializationCallbackEventArgs(contentName);
DeserializationCallback(this, e);
foundContent = e.Content;
}
if (foundContent != null)
{
DetachContentFromDockingManager(foundContent);
pane.Items.Add(foundContent);
foundContent.SetStateToDock();
if (contentElement.HasAttribute("AutoHide") &&
XmlConvert.ToBoolean(contentElement.GetAttribute("AutoHide")) &&
pane.Items.Count == 1)
toggleAutoHide = true;
//call custom layout persistence method
foundContent.RestoreLayout(contentElement);
}
} }
} }
if (pane.Items.Count > 0) if (pane.Items.Count > 0)
@ -2629,7 +2699,7 @@ namespace AvalonDock
DocumentPaneResizingPanel existingDocumentPanel = null; DocumentPaneResizingPanel existingDocumentPanel = null;
DocumentPane mainExistingDocumentPane = null; DocumentPane mainExistingDocumentPane = null;
RestoreDocumentPaneLayout(childElement, out mainExistingDocumentPane, out existingDocumentPanel); RestoreDocumentPaneLayout(childElement, out mainExistingDocumentPane, out existingDocumentPanel, dockableContents);
if (existingDocumentPanel != null) if (existingDocumentPanel != null)
{ {
@ -2723,7 +2793,7 @@ namespace AvalonDock
DocumentPaneResizingPanel existingDocumentPanel = null; DocumentPaneResizingPanel existingDocumentPanel = null;
DocumentPane mainExistingDocumentPane = null; DocumentPane mainExistingDocumentPane = null;
RestoreDocumentPaneLayout(rootElement, out mainExistingDocumentPane, out existingDocumentPanel); RestoreDocumentPaneLayout(rootElement, out mainExistingDocumentPane, out existingDocumentPanel, actualContents);
if (existingDocumentPanel != null) if (existingDocumentPanel != null)
{ {
@ -2776,14 +2846,24 @@ namespace AvalonDock
foreach (XmlElement contentElement in paneElement.ChildNodes) foreach (XmlElement contentElement in paneElement.ChildNodes)
{ {
#region Find the content to transfer #region Find the content to transfer
string contentToFindName = contentElement.GetAttribute("Name");
foreach (DockableContent content in actualContents) foreach (DockableContent content in actualContents)
{ {
if (contentElement.GetAttribute("Name") == content.Name) if (contentToFindName == content.Name)
{ {
contentToTransfer = content; contentToTransfer = content;
break; break;
} }
} }
if (contentToTransfer == null &&
DeserializationCallback != null)
{
DeserializationCallbackEventArgs e = new DeserializationCallbackEventArgs(contentToFindName);
DeserializationCallback(this, e);
contentToTransfer = e.Content;
}
#endregion #endregion

24
src/Libraries/AvalonDock/FloatingWindow.cs

@ -117,6 +117,29 @@ namespace AvalonDock
} }
#region Active Content Management
ManagedContent lastActiveContent = null;
protected override void OnActivated(EventArgs e)
{
if (Manager != null)
{
lastActiveContent = Manager.ActiveContent;
Manager.ActiveContent = HostedPane.SelectedItem as ManagedContent;
}
base.OnActivated(e);
}
protected override void OnDeactivated(EventArgs e)
{
if (Manager != null && lastActiveContent != null)
{
Manager.ActiveContent = lastActiveContent;
}
base.OnDeactivated(e);
}
#endregion
public abstract Pane ClonePane(); public abstract Pane ClonePane();
@ -368,7 +391,6 @@ namespace AvalonDock
} }
#endregion #endregion
#region INotifyPropertyChanged Members #region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;

2
src/Libraries/AvalonDock/Properties/AssemblyInfo.cs

@ -59,4 +59,4 @@ using System.Runtime.InteropServices;
// //
// You can specify all the values or you can default the Revision and Build Numbers // You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("1.2.2104")] [assembly: AssemblyVersion("1.2.2154")]

4
src/Libraries/AvalonDock/ResizingPanel.cs

@ -789,7 +789,7 @@ namespace AvalonDock
#if DEBUG #if DEBUG
Debug.Assert(_splitterList.Count == Children.Count / 2); Debug.Assert(_splitterList.Count == Children.Count / 2);
i = 0; i = 0;
while (true) while (Children.Count > 0)
{ {
Debug.Assert(Children[i] != null); Debug.Assert(Children[i] != null);
Debug.Assert(!(Children[i] is ResizingPanelSplitter)); Debug.Assert(!(Children[i] is ResizingPanelSplitter));
@ -799,7 +799,7 @@ namespace AvalonDock
Debug.Assert((Children[i] is ResizingPanelSplitter)); Debug.Assert((Children[i] is ResizingPanelSplitter));
i++; i++;
} }
#endif #endif
splitterListIsDirty = false; splitterListIsDirty = false;

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs

@ -59,7 +59,11 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
/// Gets the list box. /// Gets the list box.
/// </summary> /// </summary>
public CompletionListBox ListBox { public CompletionListBox ListBox {
get { return listBox; } get {
if (listBox == null)
ApplyTemplate();
return listBox;
}
} }
/// <summary> /// <summary>

38
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.cs

@ -32,6 +32,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
public CompletionWindow(TextArea textArea) : base(textArea) public CompletionWindow(TextArea textArea) : base(textArea)
{ {
// keep height automatic // keep height automatic
this.CloseAutomatically = true;
this.SizeToContent = SizeToContent.Height; this.SizeToContent = SizeToContent.Height;
this.Width = 175; this.Width = 175;
this.Content = completionList; this.Content = completionList;
@ -45,6 +46,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
completionList.InsertionRequested += completionList_InsertionRequested; completionList.InsertionRequested += completionList_InsertionRequested;
completionList.SelectionChanged += completionList_SelectionChanged; completionList.SelectionChanged += completionList_SelectionChanged;
AttachEvents();
} }
#region ToolTip handling #region ToolTip handling
@ -85,21 +87,26 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
{ {
// prevent CompletionWindow from growing too large // prevent CompletionWindow from growing too large
if (this.ActualHeight > 300) { if (this.ActualHeight > 300) {
this.SizeToContent = SizeToContent.Manual; if (this.SizeToContent == SizeToContent.Height)
this.SizeToContent = SizeToContent.Manual;
else if (this.SizeToContent == SizeToContent.WidthAndHeight)
this.SizeToContent = SizeToContent.Width;
this.Height = 300; this.Height = 300;
} }
base.OnSourceInitialized(e); base.OnSourceInitialized(e);
} }
/// <inheritdoc/> InputHandler myInputHandler;
protected override void AttachEvents()
void AttachEvents()
{ {
base.AttachEvents();
this.TextArea.Caret.PositionChanged += CaretPositionChanged; this.TextArea.Caret.PositionChanged += CaretPositionChanged;
this.TextArea.MouseWheel += textArea_MouseWheel; this.TextArea.MouseWheel += textArea_MouseWheel;
this.TextArea.PreviewTextInput += textArea_PreviewTextInput; this.TextArea.PreviewTextInput += textArea_PreviewTextInput;
this.TextArea.ActiveInputHandler = new InputHandler(this); myInputHandler = new InputHandler(this);
this.TextArea.ActiveInputHandler = myInputHandler;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -109,7 +116,8 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
this.TextArea.MouseWheel -= textArea_MouseWheel; this.TextArea.MouseWheel -= textArea_MouseWheel;
this.TextArea.PreviewTextInput -= textArea_PreviewTextInput; this.TextArea.PreviewTextInput -= textArea_PreviewTextInput;
base.DetachEvents(); base.DetachEvents();
this.TextArea.ActiveInputHandler = this.TextArea.DefaultInputHandler; if (this.TextArea.ActiveInputHandler == myInputHandler)
this.TextArea.ActiveInputHandler = this.TextArea.DefaultInputHandler;
} }
#region InputHandler #region InputHandler
@ -185,10 +193,22 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
return completionList.ScrollViewer ?? completionList.ListBox ?? (UIElement)completionList; return completionList.ScrollViewer ?? completionList.ListBox ?? (UIElement)completionList;
} }
/// <summary>
/// Gets/Sets whether the completion window should close automatically.
/// The default value is true.
/// </summary>
public bool CloseAutomatically { get; set; }
/// <inheritdoc/>
protected override bool CloseOnFocusLost {
get { return this.CloseAutomatically; }
}
/// <summary> /// <summary>
/// When this flag is set, code completion closes if the caret moves to the /// When this flag is set, code completion closes if the caret moves to the
/// beginning of the allowed range. This is useful in Ctrl+Space and "complete when typing", /// beginning of the allowed range. This is useful in Ctrl+Space and "complete when typing",
/// but not in dot-completion. /// but not in dot-completion.
/// Has no effect if CloseAutomatically is false.
/// </summary> /// </summary>
public bool CloseWhenCaretAtBeginning { get; set; } public bool CloseWhenCaretAtBeginning { get; set; }
@ -196,12 +216,14 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
{ {
int offset = this.TextArea.Caret.Offset; int offset = this.TextArea.Caret.Offset;
if (offset == this.StartOffset) { if (offset == this.StartOffset) {
if (CloseWhenCaretAtBeginning) if (CloseAutomatically && CloseWhenCaretAtBeginning)
Close(); Close();
return; return;
} }
if (offset < this.StartOffset || offset > this.EndOffset) { if (offset < this.StartOffset || offset > this.EndOffset) {
Close(); if (CloseAutomatically) {
Close();
}
} else { } else {
TextDocument document = this.TextArea.Document; TextDocument document = this.TextArea.Document;
if (document != null) { if (document != null) {

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindowBase.cs

@ -53,13 +53,12 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
this.AddHandler(MouseUpEvent, new MouseButtonEventHandler(OnMouseUp), true); this.AddHandler(MouseUpEvent, new MouseButtonEventHandler(OnMouseUp), true);
startOffset = endOffset = this.TextArea.Caret.Offset; startOffset = endOffset = this.TextArea.Caret.Offset;
AttachEvents();
} }
#region Event Handlers #region Event Handlers
/// <summary> void AttachEvents()
/// Attaches events to the text area.
/// </summary>
protected virtual void AttachEvents()
{ {
document = this.TextArea.Document; document = this.TextArea.Document;
if (document != null) { if (document != null) {
@ -203,7 +202,6 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
protected override void OnSourceInitialized(EventArgs e) protected override void OnSourceInitialized(EventArgs e)
{ {
base.OnSourceInitialized(e); base.OnSourceInitialized(e);
AttachEvents();
if (document != null && this.StartOffset != this.TextArea.Caret.Offset) { if (document != null && this.StartOffset != this.TextArea.Caret.Offset) {
SetPosition(new TextViewPosition(document.GetLocation(this.StartOffset))); SetPosition(new TextViewPosition(document.GetLocation(this.StartOffset)));

5
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/InsightWindow.cs

@ -31,6 +31,7 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
public InsightWindow(TextArea textArea) : base(textArea) public InsightWindow(TextArea textArea) : base(textArea)
{ {
this.CloseAutomatically = true; this.CloseAutomatically = true;
AttachEvents();
} }
/// <summary> /// <summary>
@ -44,10 +45,8 @@ namespace ICSharpCode.AvalonEdit.CodeCompletion
get { return this.CloseAutomatically; } get { return this.CloseAutomatically; }
} }
/// <inheritdoc/> void AttachEvents()
protected override void AttachEvents()
{ {
base.AttachEvents();
this.TextArea.Caret.PositionChanged += CaretPositionChanged; this.TextArea.Caret.PositionChanged += CaretPositionChanged;
} }

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/NewLineFinder.cs

@ -51,6 +51,8 @@ namespace ICSharpCode.AvalonEdit.Document
/// </summary> /// </summary>
public static string NormalizeNewLines(string input, string newLine) public static string NormalizeNewLines(string input, string newLine)
{ {
if (input == null)
return null;
Debug.Assert(IsNewLine(newLine)); Debug.Assert(IsNewLine(newLine));
SimpleSegment ds = NextNewLine(input, 0); SimpleSegment ds = NextNewLine(input, 0);
if (ds == SimpleSegment.Invalid) // text does not contain any new lines if (ds == SimpleSegment.Invalid) // text does not contain any new lines

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

@ -208,14 +208,14 @@ namespace ICSharpCode.AvalonEdit.Editing
{ {
TransformSelectedLines( TransformSelectedLines(
delegate (TextArea textArea, DocumentLine line) { delegate (TextArea textArea, DocumentLine line) {
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.ReadOnlySectionProvider.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);
}
} }
}
}, target, args, DefaultSegmentType.CurrentLine); }, target, args, DefaultSegmentType.CurrentLine);
} }
#endregion #endregion
@ -344,12 +344,14 @@ namespace ICSharpCode.AvalonEdit.Editing
string newLine = NewLineFinder.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line); string newLine = NewLineFinder.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
string text = NewLineFinder.NormalizeNewLines(Clipboard.GetText(), newLine); string text = NewLineFinder.NormalizeNewLines(Clipboard.GetText(), newLine);
bool fullLine = textArea.Options.CutCopyWholeLine && Clipboard.ContainsData(LineSelectedType); if (!string.IsNullOrEmpty(text)) {
if (fullLine) { bool fullLine = textArea.Options.CutCopyWholeLine && Clipboard.ContainsData(LineSelectedType);
DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line); if (fullLine) {
textArea.Document.Insert(currentLine.Offset, text); DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
} else { textArea.Document.Insert(currentLine.Offset, text);
textArea.ReplaceSelectionWithText(text); } else {
textArea.ReplaceSelectionWithText(text);
}
} }
textArea.Caret.BringCaretToView(); textArea.Caret.BringCaretToView();
args.Handled = true; args.Handled = true;

2
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -77,9 +77,11 @@
<Compile Include="Src\Editor\CodeCompletion\CtrlSpaceCompletionItemProvider.cs" /> <Compile Include="Src\Editor\CodeCompletion\CtrlSpaceCompletionItemProvider.cs" />
<Compile Include="Src\Editor\CodeCompletion\ICompletionItem.cs" /> <Compile Include="Src\Editor\CodeCompletion\ICompletionItem.cs" />
<Compile Include="Src\Editor\CodeCompletion\ICompletionItemList.cs" /> <Compile Include="Src\Editor\CodeCompletion\ICompletionItemList.cs" />
<Compile Include="Src\Editor\CodeCompletion\ICompletionListWindow.cs" />
<Compile Include="Src\Editor\CodeCompletion\IInsightItem.cs" /> <Compile Include="Src\Editor\CodeCompletion\IInsightItem.cs" />
<Compile Include="Src\Editor\CodeCompletion\IInsightWindow.cs" /> <Compile Include="Src\Editor\CodeCompletion\IInsightWindow.cs" />
<Compile Include="Src\Editor\CodeCompletion\IndexerInsightProvider.cs" /> <Compile Include="Src\Editor\CodeCompletion\IndexerInsightProvider.cs" />
<Compile Include="Src\Editor\CodeCompletion\ICompletionWindow.cs" />
<Compile Include="Src\Editor\CodeCompletion\MethodInsightItem.cs" /> <Compile Include="Src\Editor\CodeCompletion\MethodInsightItem.cs" />
<Compile Include="Src\Editor\CodeCompletion\MethodInsightProvider.cs" /> <Compile Include="Src\Editor\CodeCompletion\MethodInsightProvider.cs" />
<Compile Include="Src\Editor\CodeCompletion\NRefactoryCodeCompletionBinding.cs" /> <Compile Include="Src\Editor\CodeCompletion\NRefactoryCodeCompletionBinding.cs" />

26
src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditTextEditorAdapter.cs

@ -70,7 +70,7 @@ namespace ICSharpCode.SharpDevelop.Editor
public ITextEditorCaret Caret { get; private set; } public ITextEditorCaret Caret { get; private set; }
public ITextEditorOptions Options { get; private set; } public ITextEditorOptions Options { get; private set; }
public virtual IFormattingStrategy FormattingStrategy { public virtual IFormattingStrategy FormattingStrategy {
get { return DefaultFormattingStrategy.DefaultInstance; } get { return DefaultFormattingStrategy.DefaultInstance; }
} }
@ -143,14 +143,6 @@ namespace ICSharpCode.SharpDevelop.Editor
get { return null; } get { return null; }
} }
void ITextEditor.ShowCompletionWindow(ICSharpCode.TextEditor.Gui.CompletionWindow.ICompletionDataProvider provider, char ch)
{
}
public virtual void ShowCompletionWindow(ICompletionItemList data)
{
}
public object GetService(Type serviceType) public object GetService(Type serviceType)
{ {
return textEditor.TextArea.GetService(serviceType); return textEditor.TextArea.GetService(serviceType);
@ -210,5 +202,21 @@ namespace ICSharpCode.SharpDevelop.Editor
{ {
return null; return null;
} }
void ITextEditor.ShowCompletionWindow(ICSharpCode.TextEditor.Gui.CompletionWindow.ICompletionDataProvider provider, char ch)
{
}
public virtual ICompletionListWindow ActiveCompletionWindow {
get {
return null;
}
}
public virtual ICompletionListWindow ShowCompletionWindow(ICompletionItemList data)
{
return null;
}
} }
} }

24
src/Main/Base/Project/Src/Editor/CodeCompletion/ICompletionListWindow.cs

@ -0,0 +1,24 @@
/*
* Created by SharpDevelop.
* User: Daniel
* Date: 06.06.2009
* Time: 13:08
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion
{
/// <summary>
/// Represents the completion window showing a ICompletionItemList.
/// </summary>
public interface ICompletionListWindow : ICompletionWindow
{
/// <summary>
/// Gets/Sets the currently selected item.
/// </summary>
ICompletionItem SelectedItem { get; set; }
}
}

62
src/Main/Base/Project/Src/Editor/CodeCompletion/ICompletionWindow.cs

@ -0,0 +1,62 @@
/*
* Created by SharpDevelop.
* User: Daniel
* Date: 06.06.2009
* Time: 13:03
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion
{
/// <summary>
/// Base interface for IInsightWindow and ICompletionListWindow.
/// </summary>
public interface ICompletionWindow
{
/// <summary>
/// Closes the window.
/// </summary>
void Close();
/// <summary>
/// Occurs after the window was closed.
/// </summary>
event EventHandler Closed;
/// <summary>
/// Gets/Sets the width of the window.
/// double.NaN is used to represent automatic width.
///
/// For the completion list window default width is a fixed number - using automatic width
/// will reduce performance when a large number of items is shown.
/// </summary>
double Width { get; set; }
/// <summary>
/// Gets/Sets the height of the window.
/// double.NaN is used to represent automatic height.
/// </summary>
double Height { get; set; }
/// <summary>
/// Gets/Sets whether the window should close automatically.
/// The default value is true.
/// </summary>
bool CloseAutomatically { get; set; }
/// <summary>
/// Gets/Sets the start of the text range in which the window stays open.
/// Has no effect if CloseAutomatically is false.
/// </summary>
int StartOffset { get; set; }
/// <summary>
/// Gets/Sets the end of the text range in which the window stays open.
/// Has no effect if CloseAutomatically is false.
/// </summary>
int EndOffset { get; set; }
}
}

30
src/Main/Base/Project/Src/Editor/CodeCompletion/IInsightWindow.cs

@ -13,7 +13,7 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion
/// <summary> /// <summary>
/// Describes a set of insight items (e.g. multiple overloads of a method) to be displayed in the insight window. /// Describes a set of insight items (e.g. multiple overloads of a method) to be displayed in the insight window.
/// </summary> /// </summary>
public interface IInsightWindow public interface IInsightWindow : ICompletionWindow
{ {
/// <summary> /// <summary>
/// Gets the items to display. /// Gets the items to display.
@ -24,33 +24,5 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion
/// Gets/Sets the item that is currently selected. /// Gets/Sets the item that is currently selected.
/// </summary> /// </summary>
IInsightItem SelectedItem { get; set; } IInsightItem SelectedItem { get; set; }
/// <summary>
/// Gets/Sets whether the insight window should close automatically.
/// The default value is true.
/// </summary>
bool CloseAutomatically { get; set; }
/// <summary>
/// Closes the insight window.
/// </summary>
void Close();
/// <summary>
/// Occurs after the insight window was closed.
/// </summary>
event EventHandler Closed;
/// <summary>
/// Gets/Sets the start of the text range in which the insight window stays open.
/// Has no effect if CloseAutomatically is false.
/// </summary>
int StartOffset { get; set; }
/// <summary>
/// Gets/Sets the end of the text range in which the insight window stays open.
/// Has no effect if CloseAutomatically is false.
/// </summary>
int EndOffset { get; set; }
} }
} }

7
src/Main/Base/Project/Src/Editor/ITextEditor.cs

@ -71,7 +71,12 @@ namespace ICSharpCode.SharpDevelop.Editor
string FileName { get; } string FileName { get; }
void ShowCompletionWindow(ICompletionItemList data); ICompletionListWindow ShowCompletionWindow(ICompletionItemList data);
/// <summary>
/// Gets the completion window that is currently open.
/// </summary>
ICompletionListWindow ActiveCompletionWindow { get; }
/// <summary> /// <summary>
/// Open a new insight window showing the specified insight items. /// Open a new insight window showing the specified insight items.

2
src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs

@ -505,7 +505,7 @@ namespace ICSharpCode.SharpDevelop.Gui
foreach (FileDescriptionTemplate newfile in item.Template.FileDescriptionTemplates) { foreach (FileDescriptionTemplate newfile in item.Template.FileDescriptionTemplates) {
if (!IsFilenameAvailable(StringParser.Parse(newfile.Name))) { if (!IsFilenameAvailable(StringParser.Parse(newfile.Name))) {
MessageService.ShowError("Filename " + StringParser.Parse(newfile.Name) + " is in use.\nChoose another one"); MessageService.ShowError(string.Format("Filename {0} is in use.\nChoose another one", StringParser.Parse(newfile.Name))); // TODO : translate
return; return;
} }
} }

2
src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskView.cs

@ -276,7 +276,7 @@ namespace ICSharpCode.SharpDevelop.Gui
ListViewItem item = new ListViewItem(new string[] { ListViewItem item = new ListViewItem(new string[] {
String.Empty, String.Empty,
(task.Line + 1).ToString(), task.Line.ToString(),
FormatDescription(task.Description), FormatDescription(task.Description),
fileName, fileName,
path path

7
src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs

@ -9,8 +9,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
using System.IO;
namespace ICSharpCode.SharpDevelop namespace ICSharpCode.SharpDevelop
{ {
@ -35,6 +36,10 @@ namespace ICSharpCode.SharpDevelop
} }
} }
public string AssemblyName {
get { return project.AssemblyName; }
}
bool initializing; bool initializing;
public override string ToString() public override string ToString()

9
src/Main/Base/Project/Src/TextEditor/Gui/TextEditorAdapter.cs

@ -151,11 +151,18 @@ namespace ICSharpCode.SharpDevelop
} }
} }
public void ShowCompletionWindow(ICompletionItemList items) public ICompletionListWindow ActiveCompletionWindow {
get {
return null;
}
}
public ICompletionListWindow ShowCompletionWindow(ICompletionItemList items)
{ {
if (sdtac != null) { if (sdtac != null) {
sdtac.ShowCompletionWindow(new CompletionItemListAdapter(items), '.'); sdtac.ShowCompletionWindow(new CompletionItemListAdapter(items), '.');
} }
return null;
} }
public string GetWordBeforeCaret() public string GetWordBeforeCaret()

7
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs

@ -1020,5 +1020,12 @@ namespace ICSharpCode.SharpDevelop.Dom
} }
} }
} }
/// <inheritdoc/>
public virtual string AssemblyName {
get {
return null;
}
}
} }
} }

7
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs

@ -115,6 +115,13 @@ namespace ICSharpCode.SharpDevelop.Dom
/// Gets whether internals in the project content are visible to the other project content. /// Gets whether internals in the project content are visible to the other project content.
/// </summary> /// </summary>
bool InternalsVisibleTo(IProjectContent otherProjectContent); bool InternalsVisibleTo(IProjectContent otherProjectContent);
/// <summary>
/// Gets the name of the assembly.
/// </summary>
string AssemblyName {
get;
}
} }
[Flags] [Flags]

6
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/ReflectionProjectContent.cs

@ -17,6 +17,7 @@ namespace ICSharpCode.SharpDevelop.Dom
public class ReflectionProjectContent : DefaultProjectContent public class ReflectionProjectContent : DefaultProjectContent
{ {
string assemblyFullName; string assemblyFullName;
string assemblyName;
DomAssemblyName[] referencedAssemblyNames; DomAssemblyName[] referencedAssemblyNames;
ICompilationUnit assemblyCompilationUnit; ICompilationUnit assemblyCompilationUnit;
string assemblyLocation; string assemblyLocation;
@ -34,6 +35,10 @@ namespace ICSharpCode.SharpDevelop.Dom
} }
} }
public override string AssemblyName {
get { return assemblyName; }
}
/// <summary> /// <summary>
/// Gets the list of assembly names referenced by this project content. /// Gets the list of assembly names referenced by this project content.
/// </summary> /// </summary>
@ -110,6 +115,7 @@ namespace ICSharpCode.SharpDevelop.Dom
this.registry = registry; this.registry = registry;
this.assemblyFullName = assemblyFullName; this.assemblyFullName = assemblyFullName;
this.assemblyName = (assemblyFullName.IndexOf(',') > -1) ? assemblyFullName.Substring(0, assemblyFullName.IndexOf(',')) : assemblyFullName;
this.referencedAssemblyNames = referencedAssemblies; this.referencedAssemblyNames = referencedAssemblies;
this.assemblyLocation = assemblyLocation; this.assemblyLocation = assemblyLocation;
this.assemblyCompilationUnit = new DefaultCompilationUnit(this); this.assemblyCompilationUnit = new DefaultCompilationUnit(this);

Loading…
Cancel
Save