diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs index 64eda64168..408ba9a285 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs @@ -354,5 +354,11 @@ namespace PythonBinding.Tests.Utils { throw new NotImplementedException(); } + + public string AssemblyName { + get { + throw new NotImplementedException(); + } + } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs index 8b1529bbc2..62bb433407 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs @@ -18,85 +18,134 @@ namespace ICSharpCode.XamlBinding.Tests public class UtilsTests { [Test] - public void XmlNamespacesForOffsetSimple() + public void DiffTestSimple() { - string xaml = File.ReadAllText("Test1.xaml"); - int offset = xaml.IndexOf("CheckBox") + "CheckBox ".Length; + string xaml = ""; + int offset = " { - {"xmlns", "http://schemas.microsoft.com/netfx/2007/xaml/presentation"}, - {"xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml"} - }; + int actualResult = Utils.GetOffsetFromValueStart(xaml, offset); - var result = Utils.GetXmlNamespacesForOffset(xaml, offset); + Assert.AreEqual(expectedResult, actualResult); + } + + [Test] + public void DiffTestSimple2() + { + string xaml = ""; + int offset = " { - {"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"} - }; + 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 = 1; + int col = 1; - foreach (var p in result) - Debug.Print(p.Key + " " + p.Value); + int result = Utils.GetOffsetFromFilePos(text, line, col); - Assert.AreEqual(expectedResult, result, "Is not equal"); + Assert.AreEqual(expected, result); } [Test] - public void XmlNamespacesForOffsetComplex() + public void GetOffsetTest2() { - string xaml = File.ReadAllText("Test3.xaml"); - int offset = xaml.IndexOf("CheckBox") + "CheckBox ".Length; + 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 = 4; + int line = 1; + int col = 5; - var expectedResult = new Dictionary { - {"xmlns", "http://schemas.microsoft.com/netfx/2007/xaml/presentation"}, - {"xmlns:x", "clr-namespace:ICSharpCode.Profiler.Controls;assembly=ICSharpCode.Profiler.Controls"} - }; + int result = Utils.GetOffsetFromFilePos(text, line, col); + + 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) - Debug.Print(p.Key + " " + p.Value); + int result = Utils.GetOffsetFromFilePos(text, line, col); - Assert.AreEqual(expectedResult, result, "Is not equal"); + Assert.AreEqual(expected, result); } [Test] - public void DiffTestSimple() + public void GetOffsetTest4() { - string xaml = ""; - int offset = ""; - int offset = " - ..\..\..\..\Tools\NUnit\nunit.framework.dll - False + ..\..\..\..\..\bin\Tools\NUnit\nunit.framework.dll diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XmlTests.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XmlTests.cs index 6731151cb0..ebc98f21f9 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XmlTests.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XmlTests.cs @@ -66,6 +66,8 @@ namespace ICSharpCode.XamlBinding.Tests int offset = "$Revision$ // -using ICSharpCode.SharpDevelop.Editor.CodeCompletion; +using ICSharpCode.SharpDevelop.Project; using System; using System.Collections.Generic; using System.Diagnostics; @@ -15,6 +15,7 @@ using System.Xml; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.XmlEditor; using LoggingService = ICSharpCode.Core.LoggingService; @@ -32,37 +33,93 @@ namespace ICSharpCode.XamlBinding static readonly List standardAttributes = new List { new DefaultCompletionItem("xmlns:") }; + + static readonly List xamlNamespaceAttributes = new List { + "Class", "ClassModifier", "FieldModifier", "Name", "Subclass", "TypeArguments", "Uid" + }; #endregion 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 = editor.Caret.Offset; - XamlResolver resolver = new XamlResolver(); + int offset = Utils.GetOffsetFromFilePos(text, line, col); - ParseInformation info = ParserService.GetParseInformation(editor.FileName); + ParseInformation info = ParserService.GetParseInformation(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); - ResolveResult rr = 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 xmlnsDefs = new Dictionary(); - if (!string.IsNullOrEmpty(attribute)) { - rr = resolver.Resolve(new ExpressionResult(attribute, cxt) { Region = new DomRegion(1,1) }, info, text); + using (XmlTextReader reader = Utils.CreateReaderAtTarget(text, line, col)) { + 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); XamlContextDescription description = XamlContextDescription.InTag; if (path == null || path.Elements.Count == 0) { - description = XamlContextDescription.AtTag; + description = XamlContextDescription.None; path = XmlParser.GetParentElementPath(text.Substring(0, offset)); } else { int ltOffset = XmlParser.GetActiveElementStartIndex(text, offset); @@ -71,7 +128,7 @@ namespace ICSharpCode.XamlBinding else { string space = text.Substring(ltOffset + 1, offset - ltOffset - 1); 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; } } @@ -85,47 +142,116 @@ namespace ICSharpCode.XamlBinding if (Utils.IsInsideXmlComment(text, offset)) description = XamlContextDescription.InComment; - var context = new XamlContext() { + Dictionary xmlnsDefs = new Dictionary(); + + using (XmlTextReader reader = Utils.CreateReaderAtTarget(text, editor.Caret.Line, editor.Caret.Column)) { + xmlnsDefs.AddRange(reader.GetNamespacesInScope(XmlNamespaceScope.All)); + } + + var context = new XamlCompletionContext() { PressedKey = typedValue, Description = description, - ResolvedExpression = rr, AttributeName = attribute, AttributeValue = value, RawAttributeValue = attributeValue, 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; } - static List CreateListForAttributeName(ParseInformation parseInfo, XamlExpressionContext context, string[] existingItems) + static List CreateListForAttributeName(XamlCompletionContext context, string[] existingItems) { - if (context.ElementPath.Elements.Count == 0) - return null; - QualifiedName lastElement = context.ElementPath.Elements[context.ElementPath.Elements.Count - 1]; - XamlCompilationUnit cu = parseInfo.BestCompilationUnit as XamlCompilationUnit; + QualifiedName lastElement = context.Path.Elements.LastOrDefault(); + XamlCompilationUnit cu = context.ParseInformation.BestCompilationUnit as XamlCompilationUnit; if (cu == null) return null; - IReturnType rt = cu.CreateType(lastElement.Namespace, lastElement.Name); + IReturnType rt = cu.CreateType(lastElement.Namespace, lastElement.Name.Trim('.')); if (rt == null) return null; var list = new List(); + + 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()) { 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()) { if (e.IsPublic && !existingItems.Contains(e.Name)) { - list.Add(new XamlCompletionItem(e)); + list.Add(new XamlCodeCompletionItem(e)); } } return list; } + public static IEnumerable CreateListForXmlnsCompletion(IProjectContent projectContent) + { + List list = new List(); + + 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(); + } + + sealed class XmlnsEqualityComparer : IEqualityComparer { + 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 { + 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) { if (r.LineNumber > caretLine) @@ -136,18 +262,18 @@ namespace ICSharpCode.XamlBinding return false; } - public static IList CreateListForElement(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn) + public static IList CreateListForElement(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn, bool addOpeningBrace) { var items = GetClassesFromContext(parseInfo, fileContent, caretLine, caretColumn); var result = new List(); foreach (var ns in items) { - result.AddRange(from c in ns.Value - where (c.ClassType == ClassType.Class && - !c.IsAbstract && !c.IsStatic && - !c.ClassInheritanceTree.Any(b => b.FullyQualifiedName == "System.Attribute") && - c.Methods.Any(m => m.IsConstructor && m.IsPublic)) - select (new XamlCompletionItem(c, ns.Key) as ICompletionItem) + result.AddRange((from c in ns.Value + where (c.ClassType == ClassType.Class && + !c.IsAbstract && !c.IsStatic && + !c.ClassInheritanceTree.Any(b => b.FullyQualifiedName == "System.Attribute") && + c.Methods.Any(m => m.IsConstructor && m.IsPublic)) + select (new XamlCodeCompletionItem(c, ns.Key, addOpeningBrace))).Cast() ); } @@ -156,48 +282,61 @@ namespace ICSharpCode.XamlBinding public static IList 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 - .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")) .Select( selItem => { - var it = selItem as XamlCompletionItem; + var it = selItem as XamlCodeCompletionItem; string text = it.Text; if (it.Text.EndsWith("Extension", StringComparison.Ordinal)) text = text.Remove(it.Text.Length - "Extension".Length); - return new XamlCompletionItem(text, it.Entity) as ICompletionItem; + return new XamlCodeCompletionItem(it.Entity, text); } - ); + ) + .Cast(); return neededItems.ToList(); } - public static ICompletionItemList CreateListForContext(ITextEditor editor, XamlContext context) + public static ICompletionItemList CreateListForContext(ITextEditor editor, XamlCompletionContext context) { XamlCompletionItemList list = new XamlCompletionItemList(); ParseInformation info = ParserService.GetParseInformation(editor.FileName); switch (context.Description) { + case XamlContextDescription.None: + if (context.Forced) { + list.Items.AddRange(standardElements.Select(item => new DefaultCompletionItem("<" + item.Text)).Cast()); + list.Items.AddRange(CreateListForElement(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, true)); + } + break; case XamlContextDescription.AtTag: 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; case XamlContextDescription.InTag: - var existing = Utils.GetListOfExistingAttributeNames(editor.Document.Text, editor.Caret.Offset); - list.Items.AddRange(CreateListForAttributeName(info, new XamlExpressionContext(context.Path, null, false), existing)); + var existingAttribs = Utils.GetListOfExistingAttributeNames(editor.Document.Text, editor.Caret.Line, editor.Caret.Column); + list.Items.AddRange(CreateListForAttributeName(context, existingAttribs)); 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.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(GetListOfAttachedEvents(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, existingAttribs)); } } + list.Items.AddRange(standardAttributes); break; case XamlContextDescription.InAttributeValue: @@ -210,10 +349,24 @@ namespace ICSharpCode.XamlBinding return list; } - public static IEnumerable 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 CreateMarkupExtensionInsight(XamlCompletionContext context, ParseInformation info, ITextEditor editor) { - var markup = GetInnermostMarkup(context.AttributeValue.ExtensionValue); - var trr = ResolveMarkupExtensionType(markup, info, editor, context.Path); + var markup = Utils.GetInnermostMarkup(context.AttributeValue.ExtensionValue); + var trr = ResolveMarkupExtensionType(markup, context); if (trr != null) { 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 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) { 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); 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()); } } - 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) { case "System.Windows.Markup.ArrayExtension": @@ -272,12 +425,12 @@ namespace ICSharpCode.XamlBinding break; case "System.Windows.Markup.StaticExtension": 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; case "System.Windows.Markup.TypeExtension": if (context.AttributeValue.ExtensionValue.PositionalArguments.Count == 1 && context.PressedKey == ' ') break; 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(); if (selItem != null && selItem.IsString) { string s = selItem.StringValue; @@ -318,7 +471,7 @@ namespace ICSharpCode.XamlBinding } } - public static IEnumerable MemberCompletion(ITextEditor editor, IReturnType type) + public static IEnumerable MemberCompletion(XamlCompletionContext context, IReturnType type) { if (type == null || type.GetUnderlyingClass() == null) yield break; @@ -328,7 +481,7 @@ namespace ICSharpCode.XamlBinding switch (c.ClassType) { case ClassType.Enum: foreach (IField f in c.Fields) - yield return new XamlCompletionItem(f); + yield return new XamlCodeCompletionItem(f); break; case ClassType.Struct: if (c.FullyQualifiedName == "System.Boolean") { @@ -339,60 +492,79 @@ namespace ICSharpCode.XamlBinding case ClassType.Delegate: IMethod invoker = c.Methods.Where(method => method.Name == "Invoke").FirstOrDefault(); if (invoker != null) { - var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); - if (path != null && path.Elements.Count > 0) { - var item = path.Elements[path.Elements.Count - 1]; - string attribute = XmlParser.GetAttributeNameAtIndex(editor.Document.Text, editor.Caret.Offset); - var e = ResolveAttribute(attribute, editor) as IEvent; - if (e == null) + if (context.Path != null) { + var item = context.Path.Elements.LastOrDefault(); + var evt = ResolveAttribute(context.AttributeName, context) as IEvent; + if (evt == null) + break; + + int offset = XmlParser.GetActiveElementStartIndex(context.Editor.Document.Text, context.Editor.Caret.Offset); + + if (offset == -1) 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; } } 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(); - - var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); - 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; + var exp = new ExpressionResult(attribute, context); + var mrr = resolver.Resolve(exp, context.ParseInformation, context.Editor.Document.Text) as MemberResolveResult; 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(); if (context.PressedKey == '.') { 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) - list.Items.AddRange(MemberCompletion(editor, rr.ResolvedType)); + list.Items.AddRange(MemberCompletion(context, rr.ResolvedType)); } } else { if (selItem != null && selItem.IsString) { int index = selItem.StringValue.IndexOf('.'); 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) { - list.Items.AddRange(MemberCompletion(editor, rr.ResolvedType)); + list.Items.AddRange(MemberCompletion(context, rr.ResolvedType)); list.PreselectionLength = selItem.StringValue.Length - index - 1; list.SuggestedItem = list.Items.FirstOrDefault(item => item.Text.StartsWith(selItem.StringValue.Substring(index + 1), StringComparison.OrdinalIgnoreCase)); } else - DoStaticTypeCompletion(selItem, list, info, editor); + DoStaticTypeCompletion(selItem, list, context.ParseInformation, context.Editor); } 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); 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()); } if (selItem != null && selItem.IsString) { 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 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; } @@ -421,33 +595,22 @@ namespace ICSharpCode.XamlBinding { 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(); - TypeResolveResult trr = resolver.Resolve(new ExpressionResult(markup.ExtensionType, new XamlExpressionContext(path, null, false)), info, 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; + 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", context), context.ParseInformation, context.Editor.Document.Text) as TypeResolveResult; 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 AddMatchingEventHandlers(ITextEditor editor, IMethod delegateInvoker) { ParseInformation p = ParserService.GetParseInformation(editor.FileName); @@ -473,7 +636,7 @@ namespace ICSharpCode.XamlBinding break; } if (equal) { - yield return new XamlCompletionItem(m); + yield return new XamlCodeCompletionItem(m); } } } @@ -491,15 +654,22 @@ namespace ICSharpCode.XamlBinding return result; } - static IDictionary> 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))) { - try { - r.WhitespaceHandling = WhitespaceHandling.Significant; - // move reader to correct position - while (r.Read() && !IsReaderAtTarget(r, caretLine, caretColumn)) { } - } catch (XmlException) {} + using (XmlTextReader r = Utils.CreateReaderAtTarget(fileContent, caretLine, caretColumn)) { + foreach (var item in r.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml)) { + if (item.Value == @namespace) { + return item.Key; + } + } + return string.Empty; + } + } + + static IDictionary> GetClassesFromContext(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn) + { + using (XmlTextReader r = Utils.CreateReaderAtTarget(fileContent, caretLine, caretColumn)) { IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent; var result = new Dictionary>(); @@ -514,15 +684,8 @@ namespace ICSharpCode.XamlBinding static List GetListOfAttachedProperties(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn, string[] existingItems) { - using (XmlTextReader r = new XmlTextReader(new StringReader(fileContent))) { - try { - r.WhitespaceHandling = WhitespaceHandling.Significant; - // move reader to correct position - while (r.Read() && !IsReaderAtTarget(r, caretLine, caretColumn)) { } - } - catch (XmlException) { - } - var result = new List(); + using (XmlTextReader r = Utils.CreateReaderAtTarget(fileContent, caretLine, caretColumn)) { + List result = new List(); IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent; foreach (var ns in r.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml)) { @@ -561,10 +724,11 @@ namespace ICSharpCode.XamlBinding string name = (!string.IsNullOrEmpty(ns.Key)) ? ns.Key + ":" : ""; string property = 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)) + .Cast() ); } } @@ -611,14 +775,15 @@ namespace ICSharpCode.XamlBinding result.AddRange(attachedEvents .Select( - item => new XamlCompletionItem( - (string.IsNullOrEmpty(ns.Key) ? "" : ns.Key + ":") + c.Name + "." + item.Name.Remove(item.Name.Length - "Event".Length), - new DefaultEvent(c, GetEventNameFromField(item)) { - ReturnType = GetAttachedEventDelegateType(item, c) - } - ) as ICompletionItem + item => new XamlCodeCompletionItem( + new DefaultEvent(c, GetEventNameFromField(item)) { + ReturnType = GetAttachedEventDelegateType(item, c) + }, + (string.IsNullOrEmpty(ns.Key) ? "" : ns.Key + ":") + c.Name + "." + item.Name.Remove(item.Name.Length - "Event".Length) + ) ) .Where(item => !existingItems.Any(str => str == item.Text)) + .Cast() ); } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs index fcf47b117b..c419fce30e 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs @@ -5,10 +5,13 @@ // $Revision$ // -using ICSharpCode.SharpDevelop; using System; +using System.Collections.Generic; using System.Linq; + +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.XmlEditor; namespace ICSharpCode.XamlBinding @@ -36,15 +39,26 @@ namespace ICSharpCode.XamlBinding return false; } - public static QualifiedName LastOrDefault(this QualifiedNameCollection collection) + public static IEnumerable RemoveEvents(this IEnumerable list) { - if (collection == null) - throw new ArgumentNullException("collection"); - - if (collection.Count > 0) - return collection[collection.Count - 1]; - - return null; + foreach (var item in list) { + if (item is XamlCodeCompletionItem) { + var comItem = item as XamlCodeCompletionItem; + if (!(comItem.Entity is IEvent)) + yield return item; + } else yield return item; + } + } + + public static IEnumerable RemoveProperties(this IEnumerable 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; + } } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs index 2cb3d0502f..41a25a0bc1 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs @@ -20,9 +20,11 @@ namespace ICSharpCode.XamlBinding public string ExtensionType { get; set; } public IList PositionalArguments { get; private set; } public IDictionary NamedArguments { get; private set; } + + public int StartOffset { get; set; } public MarkupExtensionInfo() - : this(string.Empty, new List(), new Dictionary()) + : this(string.Empty, new List(), new Dictionary(StringComparer.OrdinalIgnoreCase)) { } @@ -39,6 +41,8 @@ namespace ICSharpCode.XamlBinding string stringValue; MarkupExtensionInfo extensionValue; + public int StartOffset { get; set; } + public bool IsString { get { return stringValue != null; } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs index 5fccb320e7..cb3dc2d3f8 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs @@ -19,22 +19,28 @@ namespace ICSharpCode.XamlBinding var tokenizer = new MarkupExtensionTokenizer(text); string argumentName = null; + int namedArgsStart = 0; var token = tokenizer.NextToken(); while (token.Kind != MarkupExtensionTokenKind.EndOfFile) { switch (token.Kind) { case MarkupExtensionTokenKind.TypeName: info.ExtensionType = token.Value; + info.StartOffset = token.StartOffset; break; 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; + namedArgsStart = token.StartOffset; break; case MarkupExtensionTokenKind.String: if (argumentName != null) { - info.NamedArguments.Add(argumentName, ParseValue(token.Value)); + info.NamedArguments.Add(argumentName, ParseValue(token.Value, namedArgsStart)); argumentName = null; } else { - info.PositionalArguments.Add(ParseValue(token.Value)); + info.PositionalArguments.Add(ParseValue(token.Value, token.StartOffset)); } break; } @@ -48,14 +54,19 @@ namespace ICSharpCode.XamlBinding } public static AttributeValue ParseValue(string text) + { + return ParseValue(text, 0); + } + + public static AttributeValue ParseValue(string text, int offset) { if (string.IsNullOrEmpty(text)) - return new AttributeValue(string.Empty); + return new AttributeValue(string.Empty) { StartOffset = offset }; if (text.StartsWith("{", StringComparison.OrdinalIgnoreCase)) - return new AttributeValue(Parse(text)); + return new AttributeValue(Parse(text)) { StartOffset = offset }; else - return new AttributeValue(text); + return new AttributeValue(text) { StartOffset = offset }; } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs index a5140fb529..97a02b18e7 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs @@ -14,6 +14,8 @@ namespace ICSharpCode.XamlBinding public MarkupExtensionTokenKind Kind { get; private set; } public string Value { get; private set; } + public int StartOffset { get; set; } + public MarkupExtensionToken(MarkupExtensionTokenKind kind, string value) { this.Kind = kind; diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs index 1caf874500..ca83d09b45 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs @@ -27,6 +27,8 @@ namespace ICSharpCode.XamlBinding string text; int pos; + int startPos; + Queue tokens = new Queue(); /// @@ -51,7 +53,7 @@ namespace ICSharpCode.XamlBinding void AddToken(MarkupExtensionTokenKind kind, string val) { - tokens.Enqueue(new MarkupExtensionToken(kind, val)); + tokens.Enqueue(new MarkupExtensionToken(kind, val) { StartOffset = startPos }); } void ParseBeginning() @@ -75,16 +77,20 @@ namespace ICSharpCode.XamlBinding case '}': AddToken(MarkupExtensionTokenKind.CloseBrace, "}"); pos++; + startPos = pos; break; case '=': AddToken(MarkupExtensionTokenKind.Equals, "="); pos++; + startPos = pos; break; case ',': AddToken(MarkupExtensionTokenKind.Comma, ","); pos++; + startPos = pos; break; default: + startPos = pos; MembernameOrString(); break; } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs index 20f9f7dc92..6b6e69d520 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs @@ -10,9 +10,11 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Xml; using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.XmlEditor; namespace ICSharpCode.XamlBinding @@ -22,129 +24,40 @@ namespace ICSharpCode.XamlBinding /// 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 (index == -1) + if (r.LineNumber > line) + return true; + else if (r.LineNumber == line) + return r.LinePosition >= col; + else 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"); - - if (text == null) - return null; - - XmlReader reader = XmlTextReader.Create(new StringReader(text)); - - try { - reader.ReadToFollowing("Test"); + try { + XmlTextReader reader = new XmlTextReader(new StringReader(text)); + reader.XmlResolver = null; + while (reader.Read() && !IsReaderAtTarget(reader, line, col)) { } + + if (!reader.MoveToFirstAttribute()) return null; do { LoggingService.Debug("name: " + reader.Name + " value: " + reader.Value); - int start = reader.Name.IndexOf(':') + 1; - string plainName = reader.Name.Substring(start, reader.Name.Length - start).ToUpperInvariant(); + string plainName = reader.Name.ToUpperInvariant(); if (plainName == name.ToUpperInvariant()) return reader.Value; } while (reader.MoveToNextAttribute()); - } catch (XmlException) { } - - return null; - } - - public static Dictionary 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(); - - 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("\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('"', '\'')); - } + } catch (XmlException e) { + Debug.Print(e.ToString()); } - return map; + return null; } public static int GetOffsetFromValueStart(string xaml, int offset) @@ -162,28 +75,29 @@ namespace ICSharpCode.XamlBinding return offset - start - 1; } - public static string[] GetListOfExistingAttributeNames(string text, int offset) + public static string[] GetListOfExistingAttributeNames(string text, int line, int col) { List list = new List(); - text = SimplifyToSingleElement(text, offset, "Test"); - if (text == null) 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 { - reader.ReadToFollowing("Test"); - - if (!reader.MoveToFirstAttribute()) - return list.ToArray(); - - do { - LoggingService.Debug("name: " + reader.Name + " value: " + reader.Value); - list.Add(reader.Name); - } while (reader.MoveToNextAttribute()); - } catch (XmlException) { } + foreach (var item in list) + Debug.Print(item); return list.ToArray(); } @@ -203,30 +117,15 @@ namespace ICSharpCode.XamlBinding 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 = list.FirstOrDefault(i => i.Value == CompletionDataHelper.XamlNamespace); - - if (item.Key.StartsWith("xmlns:", StringComparison.OrdinalIgnoreCase)) - return item.Key.Substring("xmlns:".Length); + var item = context.XmlnsDefinitions.FirstOrDefault(i => i.Value == CompletionDataHelper.XamlNamespace); + + if (item.Key != null) + return item.Key; return string.Empty; } @@ -237,7 +136,7 @@ namespace ICSharpCode.XamlBinding if (offset < 0) 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; string interestingPart = xaml.Substring(0, offset); @@ -247,5 +146,113 @@ namespace ICSharpCode.XamlBinding return interestingPart.LastIndexOf("