diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataGenerator.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataGenerator.cs index 9c4293e204..2d74612390 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataGenerator.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataGenerator.cs @@ -122,15 +122,26 @@ namespace ICSharpCode.XamlBinding { if (context.ParseInformation == null) return EmptyList.Instance; + List result = new List(); AXmlElement last = context.ParentElement; ITextEditor editor = context.Editor; compilation = SD.ParserService.GetCompilationForFile(editor.FileName); IUnresolvedFile file = context.ParseInformation.UnresolvedFile; - var items = GetClassesFromContext(context); + + foreach (string item in XamlConst.GetAllowedItems(context)) { + result.Add(new XamlCompletionItem(item)); + } + IType rt = null; if (last != null) { + if (string.Equals(last.Prefix, context.XamlNamespacePrefix, StringComparison.OrdinalIgnoreCase)) { + if (string.Equals(last.LocalName, "Members", StringComparison.OrdinalIgnoreCase)) + return result; + if (string.Equals(last.LocalName, "Code", StringComparison.OrdinalIgnoreCase)) + return result; + } // If we have an element that is not a property or an incomplete // definition => interpret element as a type. XamlResolver resolver = new XamlResolver(compilation); @@ -172,6 +183,8 @@ namespace ICSharpCode.XamlBinding .ToList(); } + var items = GetClassesFromContext(context); + foreach (var ns in items) { foreach (ITypeDefinition td in ns.Value) { if (td.Kind != TypeKind.Class && (!includeAbstract || td.Kind != TypeKind.Interface)) @@ -198,15 +211,6 @@ namespace ICSharpCode.XamlBinding // result.Add(new XamlCodeCompletionItem(itemClass, last.Prefix)); // } - var xamlItems = XamlConst.XamlNamespaceAttributes.AsEnumerable(); - - if (XamlConst.EnableXaml2009) - xamlItems = XamlConst.XamlBuiltInTypes.Concat(xamlItems); - - foreach (string item in xamlItems) { - result.Add(new XamlCompletionItem(context.XamlNamespacePrefix + ":" + item)); - } - return result; } @@ -266,8 +270,10 @@ namespace ICSharpCode.XamlBinding string xamlPrefix = context.XamlNamespacePrefix; string xKey = string.IsNullOrEmpty(xamlPrefix) ? "" : xamlPrefix + ":"; - if (lastElement.Prefix == context.XamlNamespacePrefix && XamlConst.IsBuiltin(lastElement.LocalName)) - return EmptyList.Instance; + list.AddRange(XamlConst.GetAllowedItems(context).Select(item => new XamlCompletionItem(item))); + + if (string.Equals(lastElement.Prefix, context.XamlNamespacePrefix, StringComparison.OrdinalIgnoreCase) && XamlConst.IsBuiltin(lastElement.LocalName)) + return list; if (lastElement.LocalName.EndsWith(".", StringComparison.OrdinalIgnoreCase) || context.PressedKey == '.') { if (type.Kind == TypeKind.Unknown) @@ -279,16 +285,9 @@ namespace ICSharpCode.XamlBinding } AddAttachedProperties(type.GetDefinition(), list); } else { - if (type.Kind == TypeKind.Unknown) { - list.Add(new XamlCompletionItem(xKey + "Uid")); - } else { + if (type.Kind != TypeKind.Unknown) { AddAttributes(type, list, includeEvents); list.AddRange(GetListOfAttached(context, null, includeEvents, true)); - list.AddRange( - XamlConst.XamlNamespaceAttributes - .Where(localName => XamlConst.IsAttributeAllowed(context.InRoot, localName)) - .Select(item => new XamlCompletionItem(xKey + item)) - ); } } @@ -384,11 +383,11 @@ namespace ICSharpCode.XamlBinding ITextEditor editor = context.Editor; compilation = SD.ParserService.GetCompilationForFile(editor.FileName); + string xamlPrefix = context.XamlNamespacePrefix; + string xKey = string.IsNullOrEmpty(xamlPrefix) ? "" : xamlPrefix + ":"; + if (type.Name == typeof(System.Nullable<>).Name) { - string nullExtensionName = "Null"; - if (!string.IsNullOrEmpty(context.XamlNamespacePrefix)) - nullExtensionName = context.XamlNamespacePrefix + ":" + nullExtensionName; - yield return new XamlCompletionItem("{" + nullExtensionName + "}"); + yield return new XamlCompletionItem("{" + xKey + "Null}"); type = type.TypeArguments.FirstOrDefault(); if (type == null) yield break; } @@ -397,6 +396,18 @@ namespace ICSharpCode.XamlBinding if (definition == null) yield break; + definition.IsCollectionType(); + + switch (definition.KnownTypeCode) { + case KnownTypeCode.Array: + case KnownTypeCode.ICollection: + case KnownTypeCode.ICollectionOfT: + case KnownTypeCode.IEnumerable: + case KnownTypeCode.IEnumerableOfT: + yield return new XamlCompletionItem("{" + xKey + "Array}"); + break; + } + switch (definition.Kind) { case TypeKind.Class: IType typeName; diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs index 4d848c3002..479dac1373 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Extensions.cs @@ -398,6 +398,17 @@ namespace ICSharpCode.XamlBinding return thisValue.GetAllBaseTypeDefinitions().Any(t => t.FullName == "System.Collections.IList"); } + public static bool Implements(this IType thisValue, IType interfaceType) + { + if (thisValue == null) + throw new ArgumentNullException("thisValue"); + if (interfaceType == null) + throw new ArgumentNullException("interfaceType"); + if (interfaceType.Kind != TypeKind.Interface) + throw new ArgumentException("must be TypeKind.Interface", "interfaceType"); + return thisValue.GetAllBaseTypes().Any(t => t.Equals(interfaceType)); + } + public static bool HasAttached(this ITypeDefinition thisValue, bool lookForProperties, bool lookForEvents) { if (!lookForProperties && !lookForEvents) diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs index 054ae638f6..54b80fb5de 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs @@ -83,7 +83,7 @@ namespace ICSharpCode.XamlBinding XamlCompletionItem cItem = item as XamlCompletionItem; if (xamlContext.Description == XamlContextDescription.InTag) { - if (cItem.Entity.SymbolKind == SymbolKind.Property || cItem.Entity.SymbolKind == SymbolKind.Event) { + if (cItem.Entity == null || cItem.Entity.SymbolKind == SymbolKind.Property || cItem.Entity.SymbolKind == SymbolKind.Event) { context.Editor.Document.Insert(context.EndOffset, "=\"\""); context.CompletionCharHandled = context.CompletionChar == '='; context.Editor.Caret.Offset--; diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlConst.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlConst.cs index af660c9d54..6180233a48 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlConst.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlConst.cs @@ -20,6 +20,11 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Windows; +using System.Xml.Serialization; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.SharpDevelop; namespace ICSharpCode.XamlBinding { @@ -28,33 +33,22 @@ namespace ICSharpCode.XamlBinding /// public static class XamlConst { - public const bool EnableXaml2009 = true; - // [XAML 2009] public static readonly List XamlBuiltInTypes = new List { "Object", "Boolean", "Char", "String", "Decimal", "Single", "Double", "Int16", "Int32", "Int64", "TimeSpan", "Uri", "Byte", "Array", "List", "Dictionary", - // This is no built in type, but a markup extension "Reference" }; public static readonly ReadOnlyCollection XamlNamespaceAttributes = new List { - "Class", "ClassModifier", "FieldModifier", "Name", "Subclass", "TypeArguments", "Uid", "Key" - }.AsReadOnly(); - - public static readonly ReadOnlyCollection RootOnlyElements = new List { - "Class", "ClassModifier", "Subclass" - }.AsReadOnly(); - - public static readonly ReadOnlyCollection ChildOnlyElements = new List { - "FieldModifier" + "Class", "ClassModifier", "FieldModifier", "Name", "Subclass", "TypeArguments", "Uid", "Key", "Shared" }.AsReadOnly(); /// /// values: http://schemas.microsoft.com/winfx/2006/xaml/presentation, /// http://schemas.microsoft.com/netfx/2007/xaml/presentation /// - public static readonly string[] WpfXamlNamespaces = new[] { + public static readonly string[] WpfXamlNamespaces = { "http://schemas.microsoft.com/winfx/2006/xaml/presentation", "http://schemas.microsoft.com/netfx/2007/xaml/presentation" }; @@ -88,11 +82,86 @@ namespace ICSharpCode.XamlBinding } /// - /// Returns true if the given attribute is allowed in the current element. + /// Returns the list of allow XAML2009 completion items. /// - public static bool IsAttributeAllowed(bool inRoot, string localName) + public static IEnumerable GetAllowedItems(XamlCompletionContext context) { - return inRoot ? !ChildOnlyElements.Contains(localName) : !RootOnlyElements.Contains(localName); + string xamlPrefix = context.XamlNamespacePrefix; + string xKey = string.IsNullOrEmpty(xamlPrefix) ? "" : xamlPrefix + ":"; + var compilation = SD.ParserService.GetCompilationForFile(context.Editor.FileName); + var resolver = new XamlAstResolver(compilation, context.ParseInformation); + // TODO : add support for x:Key as attribute element (XAML 2009 only) + + switch (context.Description) { + case XamlContextDescription.AtTag: + if (context.ParentElement != null && string.Equals(context.ParentElement.Name, xKey + "Members", StringComparison.OrdinalIgnoreCase)) { + yield return xKey + "Member"; + yield return xKey + "Property"; + } else if (context.ParentElement == context.RootElement && context.RootElement.Attributes.Any(attr => string.Equals(attr.Name, xKey + "Class", StringComparison.OrdinalIgnoreCase))) { + yield return xKey + "Code"; + yield return xKey + "Members"; + } else { + if (context.ParentElement != null && string.Equals(context.ParentElement.Name, xKey + "Code", StringComparison.OrdinalIgnoreCase)) + yield break; + yield return xKey + "Array"; + yield return xKey + "Boolean"; + yield return xKey + "Byte"; + yield return xKey + "Char"; + yield return xKey + "Decimal"; + yield return xKey + "Dictionary"; + yield return xKey + "Double"; + yield return xKey + "Int16"; + yield return xKey + "Int32"; + yield return xKey + "Int64"; + yield return xKey + "List"; + yield return xKey + "Object"; + yield return xKey + "Reference"; + yield return xKey + "Single"; + yield return xKey + "String"; + yield return xKey + "TimeSpan"; + yield return xKey + "Uri"; + if (context.RootElement.Attributes.Any(attr => string.Equals(attr.Name, xKey + "Class", StringComparison.OrdinalIgnoreCase))) + yield return xKey + "Members"; + } + break; + case XamlContextDescription.InTag: + yield return xKey + "Uid"; + if (context.InRoot) { + yield return xKey + "Class"; + yield return xKey + "ClassModifier"; + yield return xKey + "Subclass"; + yield return xKey + "Name"; + } else { + var resourceDictionaryType = compilation.FindType(typeof(ResourceDictionary)); + if (context.ActiveElement != null && string.Equals(context.ActiveElement.Name, xKey + "Array", StringComparison.OrdinalIgnoreCase)) { + yield return "Type"; + } else if (context.ActiveElement != null && string.Equals(context.ActiveElement.Name, xKey + "Member", StringComparison.OrdinalIgnoreCase)) { + yield return "Name"; + } else if (context.ActiveElement != null && string.Equals(context.ActiveElement.Name, xKey + "Property", StringComparison.OrdinalIgnoreCase)) { + yield return "Name"; + yield return "Type"; + } else if (context.RootElement.Attributes.Any(attr => string.Equals(attr.Name, xKey + "Class", StringComparison.OrdinalIgnoreCase))) { + yield return xKey + "FieldModifier"; + yield return xKey + "Name"; + } else { + yield return xKey + "Name"; + } + + if (context.ParentElement != null) { + var rr = resolver.ResolveElement(context.ParentElement); + if (rr != null) { + if (rr.Type.Equals(resourceDictionaryType)) { + yield return xKey + "Key"; + yield return xKey + "Shared"; + } else if (rr.Type.TypeParameterCount > 0) { + yield return xKey + "TypeArguments"; + } + } + } + } + break; + } + yield break; } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs index 8c94a8fe33..8b4829eb6f 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs @@ -94,6 +94,7 @@ namespace ICSharpCode.XamlBinding { public AXmlElement ActiveElement { get; set; } public AXmlElement ParentElement { get; set; } + public AXmlElement RootElement { get; set; } public ReadOnlyCollection Ancestors { get; set; } public AXmlAttribute Attribute { get; set; } public AttributeValue AttributeValue { get; set; } @@ -131,6 +132,7 @@ namespace ICSharpCode.XamlBinding this.AttributeValue = context.AttributeValue; this.Description = context.Description; this.ParentElement = context.ParentElement; + this.RootElement = context.RootElement; this.ParseInformation = context.ParseInformation; this.RawAttributeValue = context.RawAttributeValue; this.ValueStartOffset = context.ValueStartOffset; diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContextResolver.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContextResolver.cs index 78092c4ed0..9e53df2407 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContextResolver.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContextResolver.cs @@ -59,6 +59,7 @@ namespace ICSharpCode.XamlBinding string xamlNamespacePrefix = string.Empty; var item = currentData; + AXmlElement root = null; while (item != document) { if (item is AXmlElement) { @@ -78,8 +79,11 @@ namespace ICSharpCode.XamlBinding xamlNamespacePrefix = attr.LocalName; } - if (!wasAXmlElement && item.Parent is AXmlDocument) - isRoot = true; + if (element.Parent is AXmlDocument) { + root = element; + if (!wasAXmlElement) + isRoot = true; + } wasAXmlElement = true; } @@ -91,7 +95,7 @@ namespace ICSharpCode.XamlBinding AXmlElement active = null; AXmlElement parent = null; - + if (currentData is AXmlAttribute) { AXmlAttribute a = currentData as AXmlAttribute; int valueStartOffset = a.ValueSegment.Offset + 1; @@ -140,6 +144,7 @@ namespace ICSharpCode.XamlBinding Description = description, ActiveElement = active, ParentElement = parent, + RootElement = root, Ancestors = ancestors.AsReadOnly(), Attribute = xAttribute, InRoot = isRoot, diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignPanelHitTestResult.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignPanelHitTestResult.cs index 06c84f4cbc..dfeed9fa60 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignPanelHitTestResult.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignPanelHitTestResult.cs @@ -24,7 +24,7 @@ using ICSharpCode.WpfDesign.Adorners; namespace ICSharpCode.WpfDesign { /// - /// Describes the result of a call. + /// Describes the result of a call. /// public struct DesignPanelHitTestResult : IEquatable {