diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataGenerator.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataGenerator.cs index 4afec7d1ba..49ab1760dc 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataGenerator.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataGenerator.cs @@ -690,8 +690,8 @@ namespace ICSharpCode.XamlBinding IMethod invoker = td.GetMethods(method => method.Name == "Invoke").FirstOrDefault(); if (invoker != null && context.ActiveElement != null) { var item = context.ActiveElement; - var resolver = new XamlResolver(compilation); - var mrr = resolver.ResolveExpression(context.Attribute.ToQualifiedName().FullXmlName, context) as MemberResolveResult; + var resolver = new XamlAstResolver(compilation, context.ParseInformation); + var mrr = resolver.ResolveAttribute(context.Attribute) as MemberResolveResult; IEvent evt; if (mrr == null || (evt = mrr.Member as IEvent) == null) return EmptyList.Instance; diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs deleted file mode 100644 index 810fa619c4..0000000000 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs +++ /dev/null @@ -1,1004 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Linq; -using System.Windows.Media; - -using ICSharpCode.NRefactory.Editor; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.Dom; -using ICSharpCode.SharpDevelop.Editor; -using ICSharpCode.SharpDevelop.Editor.CodeCompletion; -using ICSharpCode.SharpDevelop.Project; - -namespace ICSharpCode.XamlBinding -{ - #if FALSE - public static class CompletionDataHelper - { - - public const bool EnableXaml2009 = true; - - static bool AllowedInElement(bool inRoot, string item) - { - return inRoot ? !ChildOnlyElements.Contains(item) : !RootOnlyElements.Contains(item); - } - - public static IEnumerable CreateListForXmlnsCompletion(IProjectContent projectContent) - { - List list = new List(); - - foreach (IProjectContent content in projectContent.ThreadSafeGetReferencedContents()) { - 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)); - } - - list.Add(new XmlnsCompletionItem(MarkupCompatibilityNamespace, true)); - - return list - .Distinct(new XmlnsEqualityComparer()) - .OrderBy(item => item, new XmlnsComparer()); - } - - public static IEnumerable GetAllTypes(XamlCompletionContext context) - { - var items = GetClassesFromContext(context); - - foreach (var ns in items) { - foreach (var c in ns.Value) { - if (c.ClassType == ClassType.Class && !c.DerivesFrom("System.Attribute")) - yield return new XamlCodeCompletionItem(c, ns.Key); - } - } - } - - public static IEnumerable CreateListOfMarkupExtensions(XamlCompletionContext context) - { - var list = CreateElementList(context, true, false); - - var neededItems = list.OfType() - .Where(i => (i.Entity as IClass).DerivesFrom("System.Windows.Markup.MarkupExtension")); - foreach (XamlCodeCompletionItem it in neededItems) { - string text = it.Text; - if (it.Text.EndsWith("Extension", StringComparison.Ordinal)) - text = text.Remove(it.Text.Length - "Extension".Length); - it.Text = text; - } - - return neededItems.Cast().Add(new XamlCompletionItem(context.XamlNamespacePrefix, XamlNamespace, "Reference")); - } - - public static IEnumerable CreateMarkupExtensionInsight(XamlCompletionContext context) - { - var markup = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset); - var type = ResolveType(markup.ExtensionType, context) ?? ResolveType(markup.ExtensionType + "Extension", context); - - if (type != null) { - var ctors = type.GetMethods() - .Where(m => m.IsPublic && m.IsConstructor && m.Parameters.Count >= markup.PositionalArguments.Count) - .OrderBy(m => m.Parameters.Count); - - foreach (var ctor in ctors) - yield return new MarkupExtensionInsightItem(ctor); - } - } - - public static ICompletionItemList CreateMarkupExtensionCompletion(XamlCompletionContext context) - { - var list = new XamlCompletionItemList(context); - string visibleValue = context.RawAttributeValue.Substring(0, Utils.MinMax(context.ValueStartOffset, 0, context.RawAttributeValue.Length)); - if (context.PressedKey == '=') - visibleValue += "="; -// context.RawAttributeValue = visibleValue; -// context.AttributeValue = MarkupExtensionParser.ParseValue(visibleValue); - var markup = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset); - var type = ResolveType(markup.ExtensionType, context) ?? ResolveType(markup.ExtensionType + "Extension", context); - - if (type == null) { - list.Items.AddRange(CreateListOfMarkupExtensions(context)); - list.PreselectionLength = markup.ExtensionType.Length; - } else { - if (markup.NamedArguments.Count == 0) { - if (DoPositionalArgsCompletion(list, context, markup, type)) - DoNamedArgsCompletion(list, context, type, markup); - } else - DoNamedArgsCompletion(list, context, type, markup); - } - - list.SortItems(); - - return list; - } - - static void DoNamedArgsCompletion(XamlCompletionItemList list, XamlCompletionContext context, IReturnType type, MarkupExtensionInfo markup) - { - if (markup.NamedArguments.Count > 0 && !context.Editor.GetWordBeforeCaret().StartsWith(",", StringComparison.OrdinalIgnoreCase)) { - int lastStart = markup.NamedArguments.Max(i => i.Value.StartOffset); - var item = markup.NamedArguments.First(p => p.Value.StartOffset == lastStart); - - if (context.RawAttributeValue.EndsWith("=", StringComparison.OrdinalIgnoreCase) || - (item.Value.IsString && item.Value.StringValue.EndsWith(context.Editor.GetWordBeforeCaretExtended(), StringComparison.Ordinal))) { - MemberResolveResult mrr = XamlResolver.ResolveMember(item.Key, context) as MemberResolveResult; - if (mrr != null && mrr.ResolvedMember != null && mrr.ResolvedMember.ReturnType != null) { - IReturnType memberType = mrr.ResolvedMember.ReturnType; - list.Items.AddRange(MemberCompletion(context, memberType, string.Empty)); - } - return; - } - } - - list.Items.AddRange(type.GetProperties().Where(p => p.CanSet && p.IsPublic).Select(p => new XamlCodeCompletionItem(p.Name + "=", p))); - } - - /// returns true if elements from named args completion should be added afterwards. - static bool DoPositionalArgsCompletion(XamlCompletionItemList list, XamlCompletionContext context, MarkupExtensionInfo markup, IType type) - { - switch (type.FullyQualifiedName) { - case "System.Windows.Markup.ArrayExtension": - case "System.Windows.Markup.NullExtension": - // x:Null/x:Array does not need completion, ignore it - break; - case "System.Windows.Markup.StaticExtension": - if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) - return DoStaticExtensionCompletion(list, context); - break; - case "System.Windows.Markup.TypeExtension": - if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) { - list.Items.AddRange(GetClassesFromContext(context).FlattenToList()); - AttributeValue selItem = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset) - .PositionalArguments.LastOrDefault(); - string word = context.Editor.GetWordBeforeCaret().TrimEnd(); - if (selItem != null && selItem.IsString && word == selItem.StringValue) { - list.PreselectionLength = selItem.StringValue.Length; - } - } - break; - default: - var ctors = type.GetMethods() - .Where(m => m.IsPublic && m.IsConstructor && m.Parameters.Count >= markup.PositionalArguments.Count + 1); - if (context.Forced) - return true; - if (ctors.Any() || markup.PositionalArguments.Count == 0) - return false; - break; - } - - return true; - } - - static IEnumerable FlattenToList(this IDictionary> data) - { - foreach (var item in data) { - foreach (IClass c in item.Value) { - yield return new XamlCodeCompletionItem(c, item.Key); - } - } - } - - public static IEnumerable MemberInsight(MemberResolveResult result) - { - switch (result.ResolvedType.FullyQualifiedName) { - case "System.Windows.Thickness": - yield return new MemberInsightItem(result.ResolvedMember, "uniformLength"); - yield return new MemberInsightItem(result.ResolvedMember, "left, top"); - yield return new MemberInsightItem(result.ResolvedMember, "left, top, right, bottom"); - break; - case "System.Windows.Size": - yield return new MemberInsightItem(result.ResolvedMember, "width, height"); - break; - case "System.Windows.Point": - yield return new MemberInsightItem(result.ResolvedMember, "x, y"); - break; - case "System.Windows.Rect": - yield return new MemberInsightItem(result.ResolvedMember, "x, y, width, height"); - break; - } - } - - public static string LookForTargetTypeValue(XamlCompletionContext context, out bool isExplicit, params string[] elementName) { - var ancestors = context.Ancestors; - - isExplicit = false; - - for (int i = 0; i < ancestors.Count; i++) { - if (ancestors[i].LocalName == "Style" && WpfXamlNamespaces.Contains(ancestors[i].Namespace)) { - isExplicit = true; - return ancestors[i].GetAttributeValue("TargetType") ?? string.Empty; - } - - if (ancestors[i].Name.EndsWithAny(elementName.Select(s => "." + s + "s"), StringComparison.Ordinal) - && !ancestors[i].Name.StartsWith("Style.", StringComparison.Ordinal)) { - return ancestors[i].Name.Remove(ancestors[i].Name.IndexOf('.')); - } - } - - return null; - } - - public static string GetTypeNameFromTypeExtension(MarkupExtensionInfo info, XamlCompletionContext context) - { - IReturnType type = CompletionDataHelper.ResolveType(info.ExtensionType, context) - ?? CompletionDataHelper.ResolveType(info.ExtensionType + "Extension", context); - - if (type == null || type.FullyQualifiedName != "System.Windows.Markup.TypeExtension") - return string.Empty; - - 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; - } - - public static bool EndsWithAny(this string thisValue, IEnumerable items, StringComparison comparison) - { - foreach (string item in items) { - if (thisValue.EndsWith(item, comparison)) - return true; - } - - return false; - } - - public static bool EndsWithAny(this string thisValue, params char[] items) - { - foreach (char item in items) { - if (thisValue.EndsWith(item.ToString())) - return true; - } - - return false; - } - - static IReturnType GetType(XamlCompletionContext context, out bool isExplicit) - { - AttributeValue value = MarkupExtensionParser.ParseValue(LookForTargetTypeValue(context, out isExplicit, "Trigger", "Setter") ?? string.Empty); - - IReturnType typeName = null; - string typeNameString = null; - - if (!value.IsString) { - typeNameString = GetTypeNameFromTypeExtension(value.ExtensionValue, context); - typeName = CompletionDataHelper.ResolveType(typeNameString, context); - } else { - typeNameString = value.StringValue; - typeName = CompletionDataHelper.ResolveType(value.StringValue, context); - } - - return typeName; - } - - public static IEnumerable MemberCompletion(XamlCompletionContext context, IReturnType type, string textPrefix) - { - if (type == null || type.GetUnderlyingClass() == null) - yield break; - - var c = type.GetUnderlyingClass(); - - if (type is ConstructedReturnType && type.TypeArgumentCount > 0 && c.FullyQualifiedName == "System.Nullable") { - ConstructedReturnType rt = type as ConstructedReturnType; - string nullExtensionName = "Null"; - if (!string.IsNullOrEmpty(context.XamlNamespacePrefix)) - nullExtensionName = context.XamlNamespacePrefix + ":" + nullExtensionName; - yield return new SpecialCompletionItem("{" + nullExtensionName + "}"); - c = rt.TypeArguments.First().GetUnderlyingClass(); - if (c == null) - yield break; - } - - bool isExplicit, showFull = false; - IReturnType typeName; - - string valueBeforeCaret = (context.ValueStartOffset > 0) ? - context.RawAttributeValue.Substring(0, context.ValueStartOffset) : ""; - - switch (c.ClassType) { - case ClassType.Class: - switch (c.FullyQualifiedName) { - case "System.String": - // return nothing - break; - case "System.Type": - foreach (var item in CreateElementList(context, true, true)) - yield return item; - break; - case "System.Windows.PropertyPath": - foreach (var item in CreatePropertyPathCompletion(context)) - yield return item; - break; - case "System.Windows.DependencyProperty": - typeName = GetType(context, out isExplicit); - - bool isReadOnly = context.ActiveElement.Name.EndsWith("Trigger"); - - if (!isExplicit && valueBeforeCaret.Contains(".")) - showFull = true; - - if (typeName != null) { - foreach (var item in typeName.GetDependencyProperties(true, !isExplicit, !isReadOnly, showFull)) - yield return item; - } - break; - case "System.Windows.RoutedEvent": - typeName = GetType(context, out isExplicit); - - if (!isExplicit && valueBeforeCaret.Contains(".")) - showFull = true; - - if (typeName != null) { - foreach (var item in typeName.GetRoutedEvents(true, !isExplicit, showFull)) - yield return item; - } - break; - case "System.Windows.Media.FontFamily": - foreach (var font in Fonts.SystemFontFamilies) - yield return new SpecialValueCompletionItem(font.FamilyNames.First().Value); - break; - default: - if (context.Description == XamlContextDescription.InMarkupExtension) { - foreach (IField f in c.Fields) - yield return new XamlCodeCompletionItem(textPrefix + f.Name, f); - foreach (IProperty p in c.Properties.Where(pr => pr.IsPublic && pr.IsStatic && pr.CanGet)) - yield return new XamlCodeCompletionItem(textPrefix + p.Name, p); - } - break; - } - break; - case ClassType.Enum: - foreach (IField f in c.Fields) - yield return new XamlCodeCompletionItem(textPrefix + f.Name, f); - foreach (IProperty p in c.Properties.Where(pr => pr.IsPublic && pr.IsStatic && pr.CanGet)) - yield return new XamlCodeCompletionItem(textPrefix + p.Name, p); - break; - case ClassType.Struct: - switch (c.FullyQualifiedName) { - case "System.Boolean": - yield return new SpecialValueCompletionItem("True"); - yield return new SpecialValueCompletionItem("False"); - break; - case "System.Windows.GridLength": - yield return new SpecialValueCompletionItem("Auto"); - yield return new SpecialValueCompletionItem("*"); - break; - } - break; - case ClassType.Delegate: - foreach (var item in CreateEventCompletion(context, c)) - yield return item; - break; - } - - var classes = c.ProjectContent.Classes.Where( - cla => (cla.FullyQualifiedName == c.FullyQualifiedName + "s" || - cla.FullyQualifiedName == c.FullyQualifiedName + "es")); - foreach (var coll in classes) { - foreach (var item in coll.Properties) - yield return new SpecialValueCompletionItem(item.Name); - foreach (var item in coll.Fields.Where(f => f.IsPublic && f.IsStatic && f.ReturnType.FullyQualifiedName == c.FullyQualifiedName)) - yield return new SpecialValueCompletionItem(item.Name); - } - } - - static IList CreatePropertyPathCompletion(XamlCompletionContext context) - { - bool isExplicit; - IReturnType typeName = GetType(context, out isExplicit); - IList list = new List(); - - string value = context.ValueStartOffset > -1 ? context.RawAttributeValue.Substring(0, Math.Min(context.ValueStartOffset + 1, context.RawAttributeValue.Length)) : ""; - - if (value.EndsWithAny(']', ')')) - return list; - - var segments = PropertyPathParser.Parse(value).ToList(); - - int completionStart; - bool isAtDot = false; - - IReturnType propertyPathType = ResolvePropertyPath(segments, context, typeName, out completionStart); - if (completionStart < segments.Count) { - PropertyPathSegment seg = segments[completionStart]; - switch (seg.Kind) { - case SegmentKind.ControlChar: - if (seg.Content == ".") { - AddAttributes(propertyPathType, list, false); - isAtDot = true; - } - break; - case SegmentKind.AttachedProperty: - AddAttributes(seg.Resolve(context, propertyPathType), list, false); - isAtDot = seg.Content.Contains("."); - break; - case SegmentKind.PropertyOrType: - AddAttributes(propertyPathType, list, false); - isAtDot = true; - break; - } - } else if (typeName != null) { - AddAttributes(typeName, list, false); - } - - if (!isAtDot) { - foreach (var item in GetAllTypes(context)) - list.Add(item); - } - - return list; - } - - static IReturnType ResolvePropertyPath(IList segments, XamlCompletionContext context, IReturnType parentType, out int lastIndex) - { - IReturnType type = parentType; - - for (lastIndex = 0; lastIndex < segments.Count - 1; lastIndex++) { - PropertyPathSegment segment = segments[lastIndex]; - switch (segment.Kind) { - case SegmentKind.AttachedProperty: - // do we need to take account of previous results? - type = segment.Resolve(context, null); - break; - case SegmentKind.ControlChar: - if (segment.Content == "[" || segment.Content == "(" || segment.Content == "/") - return null; - return type; - case SegmentKind.PropertyOrType: - type = segment.Resolve(context, type); - break; - case SegmentKind.Indexer: - if (type != null) { - IProperty prop = type.GetProperties().FirstOrDefault(p => p.IsIndexer); - if (prop != null) { - type = prop.ReturnType; - } - } - break; - case SegmentKind.SourceTraversal: - // ignore - return null; - } - } - - return type; - } - - static IReturnType Resolve(this PropertyPathSegment segment, XamlCompletionContext context, IReturnType previousType) - { - if (segment.Kind == SegmentKind.SourceTraversal) - return previousType; - if (segment.Kind == SegmentKind.ControlChar) - return previousType; - - string content = segment.Content; - - if (segment.Kind == SegmentKind.AttachedProperty && content.StartsWith("(")) { - content = content.TrimStart('('); - if (content.Contains(".")) - content = content.Remove(content.IndexOf('.')); - } - - XamlContextDescription tmp = context.Description; - context.Description = XamlContextDescription.InTag; - - ResolveResult rr = XamlResolver.Resolve(content, context); - IReturnType type = null; - - if (rr is TypeResolveResult) - type = (rr as TypeResolveResult).ResolvedType; - - if (previousType != null) { - IMember member = previousType.GetMemberByName(content); - if (member != null) - type = member.ReturnType; - } else { - if (rr is MemberResolveResult) { - MemberResolveResult mrr = rr as MemberResolveResult; - if (mrr.ResolvedMember != null) - type = mrr.ResolvedMember.ReturnType; - } - if (rr is TypeResolveResult) - type = (rr as TypeResolveResult).ResolvedType; - } - - context.Description = tmp; - return type; - } - - static IMember GetMemberByName(this IReturnType type, string name) - { - if (type == null) - throw new ArgumentNullException("type"); - - foreach (IMember member in type.GetFields()) { - if (member.Name == name) - return member; - } - - foreach (IMember member in type.GetProperties()) { - if (member.Name == name) - return member; - } - - return null; - } - - static IEnumerable CreateEventCompletion(XamlCompletionContext context, IClass c) - { - IMethod invoker = c.Methods.FirstOrDefault(method => method.Name == "Invoke"); - if (invoker != null && context.ActiveElement != null) { - var item = context.ActiveElement; - var evt = ResolveAttribute(context.Attribute.ToQualifiedName(), context) as IEvent; - if (evt == null) - return Enumerable.Empty(); - - int offset = XmlEditor.XmlParser.GetActiveElementStartIndex(context.Editor.Document.Text, context.Editor.Caret.Offset); - - if (offset == -1) - return Enumerable.Empty(); - - var loc = context.Editor.Document.OffsetToPosition(offset); - - string name = context.ActiveElement.GetAttributeValue("Name"); - if (string.IsNullOrEmpty(name)) - name = context.ActiveElement.GetAttributeValue(XamlNamespace, "Name"); - - IList list = new List(); - list.Add(new NewEventCompletionItem(evt, (string.IsNullOrEmpty(name) ? item.Name : name))); - - return CompletionDataHelper.AddMatchingEventHandlers(context, invoker).Concat(list); - } - - return Enumerable.Empty(); - } - - static IMember ResolveAttribute(QualifiedNameWithLocation attribute, XamlCompletionContext context) - { - if (attribute == null) - return null; - - return ResolveAttribute(attribute.FullXmlName, context); - } - - static IMember ResolveAttribute(string attribute, XamlCompletionContext context) - { - MemberResolveResult mrr = XamlResolver.Resolve(attribute, context) as MemberResolveResult; - - if (mrr == null) - return null; - - return mrr.ResolvedMember; - } - - static bool DoStaticExtensionCompletion(XamlCompletionItemList list, XamlCompletionContext context) - { - AttributeValue selItem = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset) - .PositionalArguments.LastOrDefault(); - if (context.PressedKey == '.') { - if (selItem != null && selItem.IsString) { - var rr = XamlResolver.Resolve(selItem.StringValue, context) as TypeResolveResult; - if (rr != null) - list.Items.AddRange(MemberCompletion(context, rr.ResolvedType, string.Empty)); - return false; - } - } else { - if (selItem != null && selItem.IsString) { - int index = selItem.StringValue.IndexOf('.'); - string s = (index > -1) ? selItem.StringValue.Substring(0, index) : selItem.StringValue; - var rr = XamlResolver.Resolve(s, context) as TypeResolveResult; - if (rr != null) { - list.Items.AddRange(MemberCompletion(context, rr.ResolvedType, (index == -1) ? "." : string.Empty)); - - list.PreselectionLength = (index > -1) ? selItem.StringValue.Length - index - 1 : 0; - - return false; - } else - DoStaticTypeCompletion(selItem, list, context); - } else { - DoStaticTypeCompletion(selItem, list, context); - } - } - - return true; - } - - static void DoStaticTypeCompletion(AttributeValue selItem, XamlCompletionItemList list, XamlCompletionContext context) - { - var items = GetClassesFromContext(context); - 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 XamlCodeCompletionItem(c, ns.Key))); - } - if (selItem != null && selItem.IsString) { - list.PreselectionLength = selItem.StringValue.Length; - } - } - - public static IType ResolveType(string typeName, XamlContext context) - { - if (context.ParseInformation == null) - return null; - - string prefix = ""; - int len = typeName.IndexOf(':'); - string name = typeName; - if (len > 0) { - prefix = typeName.Substring(0, len); - name = typeName.Substring(len + 1, name.Length - len - 1); - } - string namespaceName = ""; - if (context.XmlnsDefinitions.TryGetValue(prefix, out namespaceName)) { - IReturnType rt = cu.CreateType(namespaceName, name); - if (rt != null && rt.GetUnderlyingClass() != null) - return rt; - } - return null; - } - - public static IEnumerable AddMatchingEventHandlers(XamlCompletionContext context, IMethod delegateInvoker) - { - if (context.ParseInformation == null) - yield break; - - var unit = context.ParseInformation.CompilationUnit; - var loc = context.Editor.Caret.Position; - IClass c = unit.GetInnermostClass(loc.Line, loc.Column); - if (c == null) - yield break; - CompoundClass compound = c.GetCompoundClass() as CompoundClass; - if (compound != null) { - foreach (IClass part in compound.Parts) { - foreach (IMethod m in part.Methods) { - if (m.Parameters.Count != delegateInvoker.Parameters.Count) - continue; - - if ((m.ReturnType != null && delegateInvoker.ReturnType != null) && m.ReturnType.DotNetName != delegateInvoker.ReturnType.DotNetName) - continue; - - bool equal = m.Parameters.SequenceEqual(delegateInvoker.Parameters, new ParameterComparer()); - if (equal) { - yield return new XamlCodeCompletionItem(m); - } - } - } - } - } - - public static bool Compare(this IParameter p1, IParameter p2) - { - return (p1.ReturnType.DotNetName == p2.ReturnType.DotNetName) && - (p1.IsOut == p2.IsOut) && (p1.IsParams == p2.IsParams) && (p1.IsRef == p2.IsRef); - } - - public static bool IsPubliclySetable(this IUnresolvedProperty thisValue) - { - return thisValue.CanSet && thisValue.Setter.IsPublic; - } - - public static IEnumerable GetDependencyProperties(this IReturnType type, bool excludeSuffix, bool addType, bool requiresSetable, bool showFull) - { - foreach (var field in type.GetFields()) { - if (field.ReturnType.FullyQualifiedName != "System.Windows.DependencyProperty") - continue; - if (field.Name.Length <= "Property".Length || !field.Name.EndsWith("Property", StringComparison.Ordinal)) - continue; - string fieldName = field.Name.Remove(field.Name.Length - "Property".Length); - IProperty property = type.GetProperties().FirstOrDefault(p => p.Name == fieldName); - if (property == null) - continue; - if (requiresSetable && !property.IsPubliclySetable()) - continue; - - if (!excludeSuffix) - fieldName = field.Name; - - if (showFull) { - addType = false; - - fieldName = field.DeclaringType.Name + "." + fieldName; - } - - yield return new XamlLazyValueCompletionItem(field, fieldName, addType); - } - } - - public static IEnumerable GetRoutedEvents(this IReturnType type, bool excludeSuffix, bool addType, bool showFull) - { - foreach (var field in type.GetFields()) { - if (field.ReturnType.FullyQualifiedName != "System.Windows.RoutedEvent") - continue; - if (field.Name.Length <= "Event".Length || !field.Name.EndsWith("Event", StringComparison.Ordinal)) - continue; - string fieldName = field.Name.Remove(field.Name.Length - "Event".Length); - if (!type.GetEvents().Any(p => p.Name == fieldName)) - continue; - - if (!excludeSuffix) - fieldName = field.Name; - - if (showFull) { - addType = false; - - fieldName = field.DeclaringType.Name + "." + fieldName; - } - - yield return new XamlLazyValueCompletionItem(field, fieldName, addType); - } - } - - internal static List GetListOfAttached(XamlCompletionContext context, string prefixClassName, string prefixNamespace, bool events, bool properties) - { - List result = new List(); - - if (context.ParseInformation == null) - return result; - - IProjectContent pc = context.ProjectContent; - - if (!string.IsNullOrEmpty(prefixClassName)) { - var ns = context.XmlnsDefinitions[prefixNamespace]; - IClass c = XamlCompilationUnit.GetNamespaceMembers(pc, ns).FirstOrDefault(item => item.Name == prefixClassName); - if (c != null && c.ClassType == ClassType.Class) { - if (!c.ClassInheritanceTree.Any(b => b.FullyQualifiedName == "System.Attribute")) { - prefixNamespace = string.IsNullOrEmpty(prefixNamespace) ? prefixNamespace : prefixNamespace + ":"; - if (properties) - AddAttachedProperties(c, result, prefixNamespace, prefixNamespace + prefixClassName); - if (events) - AddAttachedEvents(c, result, prefixNamespace, prefixNamespace + prefixClassName); - } - } - } else { - foreach (var ns in context.XmlnsDefinitions) { - string key = string.IsNullOrEmpty(ns.Key) ? "" : ns.Key + ":"; - - foreach (IClass c in XamlCompilationUnit.GetNamespaceMembers(pc, ns.Value)) { - if (c.ClassType != ClassType.Class) - continue; - if (c.HasAttached(properties, events)) - result.Add(new XamlCodeCompletionItem(c, ns.Key)); - } - } - } - - return result; - } - - public static void AddAttachedProperties(IClass c, List result, string key, string prefix) - { - if (c == null) - return; - - var attachedProperties = c.Fields.Where(f => f.IsAttached(true, false)); - - int prefixLength = (prefix.Length > 0) ? prefix.Length + 1 : 0; - - result.AddRange( - attachedProperties.Select( - item => { - string property = item.Name.Remove(item.Name.Length - "Property".Length); - string name = key + c.Name + "." + item.Name.Remove(item.Name.Length - "Property".Length); - return new XamlCodeCompletionItem(name.Remove(0, prefixLength), new DefaultProperty(c, property) { ReturnType = GetAttachedPropertyType(item, c) }); - } - ) - ); - } - - static void AddAttachedEvents(IClass c, List result, string key, string prefix) - { - var attachedEvents = c.Fields.Where(f => f.IsAttached(false, true)); - - int prefixLength = (prefix.Length > 0) ? prefix.Length + 1 : 0; - - result.AddRange( - attachedEvents.Select( - item => { - string @event = item.Name.Remove(item.Name.Length - "Event".Length); - string name = key + c.Name + "." + item.Name.Remove(item.Name.Length - "Event".Length); - return new XamlCodeCompletionItem(name.Remove(0, prefixLength), new DefaultEvent(c, @event) { ReturnType = GetAttachedEventDelegateType(item, c) }); - } - ) - ); - } - - static IReturnType GetAttachedEventDelegateType(IField field, IClass c) - { - if (c == null || field == null) - return null; - - string eventName = field.Name.Remove(field.Name.Length - "Event".Length); - - IMethod method = c.Methods.FirstOrDefault(m => m.IsPublic && m.IsStatic && m.Parameters.Count == 2 && (m.Name == "Add" + eventName + "Handler" || m.Name == "Remove" + eventName + "Handler")); - - if (method == null) - return null; - - return method.Parameters[1].ReturnType; - } - - static IReturnType GetAttachedPropertyType(IField field, IClass c) - { - if (c == null || field == null) - return null; - - string propertyName = field.Name.Remove(field.Name.Length - "Property".Length); - - IMethod method = c.Methods.FirstOrDefault(m => m.IsPublic && m.IsStatic && m.Name == "Get" + propertyName); - - if (method == null) - return null; - - return method.ReturnType; - } - - static string GetEventNameFromMethod(IMethod m) - { - string mName = m.Name; - if (mName.StartsWith("Add", StringComparison.Ordinal)) - mName = mName.Remove(0, 3); - else if (mName.StartsWith("Remove", StringComparison.Ordinal)) - mName = mName.Remove(0, 6); - if (mName.EndsWith("Handler", StringComparison.Ordinal)) - mName = mName.Remove(mName.Length - "Handler".Length); - - return mName; - } - - static string GetEventNameFromField(IField f) - { - string fName = f.Name; - if (fName.EndsWith("Event", StringComparison.Ordinal)) - fName = fName.Remove(fName.Length - "Event".Length); - - return fName; - } - - static bool IsMethodFromEvent(IField f, IMethod m) - { - return GetEventNameFromField(f) == GetEventNameFromMethod(m); - } - - /// - /// Creates a IReturnType looking for a class referenced in XAML. - /// - /// The XML namespace - /// The class name - /// A new IReturnType that will search the referenced type on demand. - public IReturnType CreateType(string xmlNamespace, string className) - { - if (string.IsNullOrEmpty(className) || className.Contains(".")) - return null; - - if (xmlNamespace.StartsWith("clr-namespace:", StringComparison.OrdinalIgnoreCase)) { - return CreateClrNamespaceType(this.ProjectContent, xmlNamespace, className); - } else { - return new XamlClassReturnType(this, xmlNamespace, className); - } - } - - static IReturnType CreateClrNamespaceType(IProjectContent pc, string xmlNamespace, string className) - { - string namespaceName = GetNamespaceNameFromClrNamespace(xmlNamespace); - return new GetClassReturnType(pc, namespaceName + "." + className, 0); - } - - static string GetNamespaceNameFromClrNamespace(string xmlNamespace) - { - string namespaceName = xmlNamespace.Substring("clr-namespace:".Length); - int pos = namespaceName.IndexOf(';'); - if (pos >= 0) { - // we expect that the target type is also a reference of the project, so we - // can ignore the assembly part after the ; - namespaceName = namespaceName.Substring(0, pos); - } - return namespaceName; - } - - /// - /// Finds a type referenced in XAML. - /// - /// The XML namespace - /// The class name - /// Returns the referenced type, or null if it cannot be found. - public IReturnType FindType(string xmlNamespace, string className) - { - return FindType(this.ProjectContent, xmlNamespace, className); - } - - public static IReturnType FindType(IProjectContent pc, string xmlNamespace, string className) - { - if (pc == null) - throw new ArgumentNullException("pc"); - if (xmlNamespace == null || className == null) - return null; - if (xmlNamespace.StartsWith("clr-namespace:", StringComparison.OrdinalIgnoreCase)) { - return CreateClrNamespaceType(pc, xmlNamespace, className); - } - else { - IReturnType type = FindTypeInAssembly(pc, xmlNamespace, className); - if (type != null) - return type; - foreach (IProjectContent p in pc.ThreadSafeGetReferencedContents()) { - type = FindTypeInAssembly(p, xmlNamespace, className); - if (type != null) - return type; - } - return null; - } - } - - static IReturnType FindTypeInAssembly(IProjectContent projectContent, string xmlNamespace, string className) - { - foreach (IAttribute att in projectContent.GetAssemblyAttributes()) { - if (att.PositionalArguments.Count == 2 - && att.AttributeType.FullyQualifiedName == "System.Windows.Markup.XmlnsDefinitionAttribute") { - string namespaceName = att.PositionalArguments[1] as string; - if (xmlNamespace.Equals(att.PositionalArguments[0]) && namespaceName != null) { - IClass c = projectContent.GetClass(namespaceName + "." + className, 0); - if (c != null) - return c.DefaultReturnType; - } - } - } - return null; - } - - public static IEnumerable GetNamespaceMembers(IProjectContent pc, string xmlNamespace) - { - if (pc == null) - throw new ArgumentNullException("pc"); - - if (!string.IsNullOrEmpty(xmlNamespace)) { - if (xmlNamespace.StartsWith("clr-namespace:", StringComparison.OrdinalIgnoreCase)) - return pc.GetNamespaceContents(GetNamespaceNameFromClrNamespace(xmlNamespace)).OfType(); - else { - var list = new List(); - AddNamespaceMembersInAssembly(pc, xmlNamespace, list); - foreach (IProjectContent p in pc.ThreadSafeGetReferencedContents()) { - AddNamespaceMembersInAssembly(p, xmlNamespace, list); - } - return list.OfType(); - } - } - - return Enumerable.Empty(); - } - - static void AddNamespaceMembersInAssembly(IProjectContent projectContent, string xmlNamespace, List list) - { - foreach (IAttribute att in projectContent.GetAssemblyAttributes()) { - if (att.PositionalArguments.Count == 2 - && att.AttributeType.FullyQualifiedName == "System.Windows.Markup.XmlnsDefinitionAttribute") { - string namespaceName = att.PositionalArguments[1] as string; - if (xmlNamespace.Equals(att.PositionalArguments[0]) && namespaceName != null) { - projectContent.AddNamespaceContents(list, namespaceName, projectContent.Language, false); - } - } - } - } - } - -#endif -} diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlAstResolver.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlAstResolver.cs index 5e4e985bea..615c2b36f3 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlAstResolver.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlAstResolver.cs @@ -52,7 +52,7 @@ namespace ICSharpCode.XamlBinding return ErrorResolveResult.UnknownError; } - internal ResolveResult ResolveAttribute(AXmlAttribute attribute, int offset, CancellationToken cancellationToken = default(CancellationToken)) + internal ResolveResult ResolveAttribute(AXmlAttribute attribute, int offset = -1, CancellationToken cancellationToken = default(CancellationToken)) { IMember member = null, underlying = null; IType type = null; @@ -63,7 +63,7 @@ namespace ICSharpCode.XamlBinding propertyName = propertyName.Substring(propertyName.IndexOf('.') + 1); ITypeReference reference = XamlUnresolvedFile.CreateTypeReference(namespaceName, name); type = reference.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)); - member = FindMember(type, propertyName); + member = FindMember(type, propertyName); if (member == null) member = FindAttachedMember(type, propertyName, out underlying); } else { @@ -73,7 +73,7 @@ namespace ICSharpCode.XamlBinding } if (member == null) return new UnknownMemberResolveResult(type, propertyName, EmptyList.Instance); - if (attribute.ValueSegment.Contains(offset, 1)) + if (offset > -1 && attribute.ValueSegment.Contains(offset, 1)) return ResolveAttributeValue(member, attribute, cancellationToken); if (underlying != null) return new AttachedMemberResolveResult(new TypeResolveResult(underlying.DeclaringType), underlying, member); @@ -118,10 +118,7 @@ namespace ICSharpCode.XamlBinding IMember member = type.GetProperties(p => p.Name == propertyName).FirstOrDefault(); if (member != null) return member; - member = type.GetEvents(e => e.Name == propertyName).FirstOrDefault(); - if (member != null) - return member; - return null; + return type.GetEvents(e => e.Name == propertyName).FirstOrDefault(); } IMember FindAttachedMember(IType type, string propertyName, out IMember underlyingMember) @@ -133,7 +130,7 @@ namespace ICSharpCode.XamlBinding ITypeResolveContext localContext = new SimpleTypeResolveContext(type.GetDefinition()); if (underlyingMember != null) return new DefaultUnresolvedProperty { Name = propertyName } - .CreateResolved(localContext); + .CreateResolved(localContext); underlyingMember = type .GetMethods(m => m.IsPublic && m.IsStatic && m.Parameters.Count == 2 @@ -141,7 +138,7 @@ namespace ICSharpCode.XamlBinding .FirstOrDefault(); if (underlyingMember != null) return new DefaultUnresolvedEvent { Name = propertyName } - .CreateResolved(localContext); + .CreateResolved(localContext); return null; } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj index af81cddb02..64157a57ca 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj @@ -66,7 +66,6 @@ - diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs index 8f5dba86c0..a33a272fba 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs @@ -54,7 +54,7 @@ namespace ICSharpCode.XamlBinding if (endMarkerCount % 2 != 0) { editor.Document.Insert(editor.Caret.Offset, ch.ToString()); editor.Caret.Offset--; - this.CtrlSpace(editor); + CtrlSpace(editor); return CodeCompletionKeyPressResult.Completed; } } @@ -85,8 +85,8 @@ namespace ICSharpCode.XamlBinding } break; case XamlContextDescription.InMarkupExtension: -// if (DoMarkupExtensionCompletion(context)) -// return CodeCompletionKeyPressResult.Completed; + if (DoMarkupExtensionCompletion(context)) + return CodeCompletionKeyPressResult.Completed; break; case XamlContextDescription.InAttributeValue: if (editor.SelectionLength != 0) @@ -110,7 +110,7 @@ namespace ICSharpCode.XamlBinding editor.Document.Insert(editor.Caret.Offset, "[]"); editor.Caret.Offset--; - this.CtrlSpace(editor); + CtrlSpace(editor); return CodeCompletionKeyPressResult.EatKey; } break; @@ -147,7 +147,7 @@ namespace ICSharpCode.XamlBinding editor.Caret.Offset++; } - this.CtrlSpace(editor); + CtrlSpace(editor); return CodeCompletionKeyPressResult.EatKey; } else { DoMarkupExtensionCompletion(context); @@ -161,7 +161,7 @@ namespace ICSharpCode.XamlBinding string attributeName = (context.Attribute != null) ? context.Attribute.Name : string.Empty; if (!attributeName.StartsWith("xmlns", StringComparison.OrdinalIgnoreCase)) { - return this.CtrlSpace(editor, context) + return CtrlSpace(editor, context) ? CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion : CodeCompletionKeyPressResult.None; } @@ -186,72 +186,38 @@ namespace ICSharpCode.XamlBinding bool CtrlSpace(ITextEditor editor, XamlCompletionContext context) { if (context.Description == XamlContextDescription.InComment - || context.Description == XamlContextDescription.InCData) + || context.Description == XamlContextDescription.InCData + || context.ActiveElement == null) { return false; - if (context.ActiveElement != null) { - if (!XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset) && context.Description != XamlContextDescription.InAttributeValue) { - XamlCompletionItemList list = generator.CreateListForContext(context); - string starter = editor.GetWordBeforeCaretExtended().TrimStart('/'); - if (context.Description != XamlContextDescription.None && !string.IsNullOrEmpty(starter)) { - if (starter.Contains(".")) - list.PreselectionLength = starter.Length - starter.IndexOf('.') - 1; - else - list.PreselectionLength = starter.Length; + } + if (context.Description != XamlContextDescription.InAttributeValue) { + XamlCompletionItemList list = generator.CreateListForContext(context); + string starter = editor.GetWordBeforeCaretExtended().TrimStart('/'); + if (context.Description != XamlContextDescription.None && !string.IsNullOrEmpty(starter)) { + if (starter.Contains(".")) { + list.PreselectionLength = starter.Length - starter.IndexOf('.') - 1; + } else { + list.PreselectionLength = starter.Length; } - editor.ShowCompletionWindow(list); - return true; - } else { - // DO NOT USE CompletionDataHelper.CreateListForContext here!!! results in endless recursion!!!! - if (context.Attribute != null) { - if (!DoMarkupExtensionCompletion(context)) { - var completionList = new XamlCompletionItemList(context); - completionList.PreselectionLength = editor.GetWordBeforeCaretExtended().Length; - if ((context.ActiveElement.Name == "Setter" || context.ActiveElement.Name == "EventSetter") && (context.Attribute.Name == "Property" || context.Attribute.Name == "Value")) - DoSetterAndEventSetterCompletion(context, completionList); - else - if ((context.ActiveElement.Name.EndsWith("Trigger", StringComparison.Ordinal) || context.ActiveElement.Name == "Condition") && context.Attribute.Name == "Value") - DoTriggerCompletion(context, completionList); - else { - if (context.Attribute.Name == "xml:space") { - completionList.Items.AddRange(new[] { - new XamlCompletionItem("preserve"), - new XamlCompletionItem("default") - }); - } - XamlAstResolver resolver = new XamlAstResolver(compilation, context.ParseInformation); - var mrr = resolver.ResolveAttribute(context.Attribute, editor.Document.GetOffset(editor.Caret.Location)) as MemberResolveResult; - if (mrr != null && mrr.Type.Kind != TypeKind.Unknown) { - completionList.Items.AddRange(generator.MemberCompletion(context, mrr.Type, string.Empty)); - editor.ShowInsightWindow(generator.MemberInsight(mrr)); - switch (mrr.Type.FullName) { - case "System.Windows.PropertyPath": - string start = editor.GetWordBeforeCaretExtended(); - int index = start.LastIndexOfAny(PropertyPathTokenizer.ControlChars); - if (index + 1 < start.Length) - start = start.Substring(index + 1); - else - start = ""; - completionList.PreselectionLength = start.Length; - break; - case "System.Windows.Media.FontFamily": - string text = context.ValueStartOffset > -1 ? context.RawAttributeValue.Substring(0, Math.Min(context.ValueStartOffset, context.RawAttributeValue.Length)) : ""; - int lastComma = text.LastIndexOf(','); - completionList.PreselectionLength = lastComma == -1 ? context.ValueStartOffset : context.ValueStartOffset - lastComma - 1; - break; - } - } - } - completionList.SortItems(); - if (context.Attribute.Prefix.Equals("xmlns", StringComparison.OrdinalIgnoreCase) || context.Attribute.Name.Equals("xmlns", StringComparison.OrdinalIgnoreCase)) - completionList.Items.AddRange(generator.CreateListForXmlnsCompletion(compilation)); - ICompletionListWindow window = editor.ShowCompletionWindow(completionList); - if ((context.Attribute.Prefix.Equals("xmlns", StringComparison.OrdinalIgnoreCase) || context.Attribute.Name.Equals("xmlns", StringComparison.OrdinalIgnoreCase)) && window != null) - window.Width = 400; - return completionList.Items.Any(); - } - return true; + } + editor.ShowCompletionWindow(list); + return true; + } + // DO NOT USE generator.CreateListForContext here!!! results in endless recursion!!!! + if (context.Attribute != null) { + if (!DoMarkupExtensionCompletion(context)) { + var completionList = new XamlCompletionItemList(context); + completionList.PreselectionLength = editor.GetWordBeforeCaretExtended().Length; + if ((context.ActiveElement.Name == "Setter" || context.ActiveElement.Name == "EventSetter") && (context.Attribute.Name == "Property" || context.Attribute.Name == "Value")) { + DoSetterAndEventSetterCompletion(context, completionList); + } else if ((context.ActiveElement.Name.EndsWith("Trigger", StringComparison.Ordinal) || context.ActiveElement.Name == "Condition") && context.Attribute.Name == "Value") { + DoTriggerCompletion(context, completionList); + } else if (!DoAttributeCompletion(context, completionList)) { + DoXmlAttributeCompletion(context, completionList); } + return completionList.Items.Any(); } + return true; } return false; } @@ -357,6 +323,58 @@ namespace ICSharpCode.XamlBinding } } + bool DoAttributeCompletion(XamlCompletionContext context, XamlCompletionItemList completionList) + { + XamlAstResolver resolver = new XamlAstResolver(compilation, context.ParseInformation); + ITextEditor editor = context.Editor; + var mrr = resolver.ResolveAttribute(context.Attribute) as MemberResolveResult; + if (mrr != null && mrr.Type.Kind != TypeKind.Unknown) { + completionList.Items.AddRange(generator.MemberCompletion(context, mrr.Type, string.Empty)); + editor.ShowInsightWindow(generator.MemberInsight(mrr)); + editor.ShowCompletionWindow(completionList); + switch (mrr.Type.FullName) { + case "System.Windows.PropertyPath": + string start = editor.GetWordBeforeCaretExtended(); + int index = start.LastIndexOfAny(PropertyPathTokenizer.ControlChars); + if (index + 1 < start.Length) { + start = start.Substring(index + 1); + } + else { + start = ""; + } + completionList.PreselectionLength = start.Length; + break; + case "System.Windows.Media.FontFamily": + string text = context.ValueStartOffset > -1 ? context.RawAttributeValue.Substring(0, Math.Min(context.ValueStartOffset, context.RawAttributeValue.Length)) : ""; + int lastComma = text.LastIndexOf(','); + completionList.PreselectionLength = lastComma == -1 ? context.ValueStartOffset : context.ValueStartOffset - lastComma - 1; + break; + } + } + + return completionList.Items.Any(); + } + + void DoXmlAttributeCompletion(XamlCompletionContext context, XamlCompletionItemList completionList) + { + if (context.Attribute.Name == "xml:space") { + completionList.Items.AddRange(new[] { + new XamlCompletionItem("preserve"), + new XamlCompletionItem("default") + }); + } + + if (context.Attribute.Prefix.Equals("xmlns", StringComparison.OrdinalIgnoreCase) || + context.Attribute.Name.Equals("xmlns", StringComparison.OrdinalIgnoreCase)) { + completionList.Items.AddRange(generator.CreateListForXmlnsCompletion(compilation)); + + ICompletionListWindow window = context.Editor.ShowCompletionWindow(completionList); + if (window != null) { + window.Width = 400; + } + } + } + bool DoMarkupExtensionCompletion(XamlCompletionContext context) { if (context.Description == XamlContextDescription.InMarkupExtension && context.AttributeValue != null && !context.AttributeValue.IsString) { diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs index 691c7948bb..ee1e82212c 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs @@ -10,6 +10,7 @@ using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Project; using ICSharpCode.XmlEditor; namespace ICSharpCode.XamlBinding @@ -50,170 +51,96 @@ namespace ICSharpCode.XamlBinding return text.Length - i - 1; } -// public override void Complete(CompletionContext context, ICompletionItem item) -// { -// using (context.Editor.Document.OpenUndoGroup()) { -// base.Complete(context, item); -// -// XamlCompletionContext xamlContext = CompletionDataHelper.ResolveCompletionContext(context.Editor, context.CompletionChar); -// -// if (xamlContext.Description == XamlContextDescription.None && (context.StartOffset <= 0 || context.Editor.Document.GetCharAt(context.StartOffset - 1) != '<')) { -// context.Editor.Document.Insert(context.StartOffset, "<"); -// context.EndOffset++; -// } -// -// if (item is XamlCodeCompletionItem) { -// XamlCodeCompletionItem cItem = item as XamlCodeCompletionItem; -// -// if (cItem.Entity is IProperty || cItem.Entity is IEvent) { -// if (xamlContext.Description == XamlContextDescription.InTag) { -// context.Editor.Document.Insert(context.EndOffset, "=\"\""); -// context.CompletionCharHandled = context.CompletionChar == '='; -// context.Editor.Caret.Offset--; -// new XamlCodeCompletionBinding().CtrlSpace(context.Editor); -// } else if (xamlContext.Description == XamlContextDescription.InMarkupExtension && !string.IsNullOrEmpty(xamlContext.RawAttributeValue)) { -// string valuePart = xamlContext.RawAttributeValue.Substring(0, xamlContext.ValueStartOffset); -// AttributeValue value = MarkupExtensionParser.ParseValue(valuePart); -// -// if (value != null && !value.IsString) { -// var markup = Utils.GetMarkupExtensionAtPosition(value.ExtensionValue, context.Editor.Caret.Offset); -// if (markup.NamedArguments.Count > 0 || markup.PositionalArguments.Count > 0) { -// int oldOffset = context.Editor.Caret.Offset; -// context.Editor.Caret.Offset = context.StartOffset; -// string word = context.Editor.GetWordBeforeCaret().TrimEnd(); -// int spaces = CountWhiteSpacesAtEnd(context.Editor.GetWordBeforeCaret()); -// int typeNameStart = markup.ExtensionType.IndexOf(':') + 1; -// -// if (!(word == "." || word == "," || word == ":") && markup.ExtensionType.Substring(typeNameStart, markup.ExtensionType.Length - typeNameStart) != word) { -// context.Editor.Document.Replace(context.Editor.Caret.Offset - spaces, spaces, ", "); -// oldOffset += (2 - spaces); -// } -// -// context.Editor.Caret.Offset = oldOffset; -// } -// } -// -// if (cItem.Text.EndsWith("=", StringComparison.OrdinalIgnoreCase)) -// new XamlCodeCompletionBinding().CtrlSpace(context.Editor); -// } -// } -// } -// -// if (item is NewEventCompletionItem) { -// NewEventCompletionItem eventItem = item as NewEventCompletionItem; -// int discriminator; -// if (CreateEventHandlerCode(context, eventItem, out discriminator)) { -// if (discriminator > 0) -// context.Editor.Document.Insert(context.EndOffset, discriminator.ToString()); -// } -// } -// -// if (item is XmlnsCompletionItem) { -// context.Editor.Caret.Offset++; -// } -// -// if (item is XamlCompletionItem && xamlContext.Description == XamlContextDescription.InTag) { -// context.Editor.Document.Insert(context.EndOffset, "=\"\""); -// context.Editor.Caret.Offset--; -// new XamlCodeCompletionBinding().CtrlSpace(context.Editor); -// } -// -// switch (item.Text) { -// case "![CDATA[": -// context.Editor.Document.Insert(context.Editor.Caret.Offset, "]]>"); -// context.Editor.Caret.Offset -= 3; -// break; -// case "?": -// context.Editor.Document.Insert(context.Editor.Caret.Offset, "?>"); -// context.Editor.Caret.Offset -= 2; -// break; -// case "!--": -// context.Editor.Document.Insert(context.Editor.Caret.Offset, " -->"); -// context.Editor.Caret.Offset -= 4; -// break; -// } -// -// if (item.Text.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { -// context.Editor.Document.Insert(context.EndOffset, ">"); -// context.CompletionCharHandled = context.CompletionChar == '>'; -// context.Editor.Caret.Offset++; -// } -// } -// } -// -// static bool CreateEventHandlerCode(CompletionContext context, NewEventCompletionItem completionItem, out int discriminator) -// { -// ParseInformation p = ParserService.GetParseInformation(context.Editor.FileName); -// var unit = p.SyntaxTree; -// var loc = context.Editor.Document.OffsetToPosition(context.StartOffset); -// IClass c = unit.GetInnermostClass(loc.Line, loc.Column); -// -// discriminator = 1; -// -// if (c == null) -// return false; -// IMethod initializeComponent = c.Methods[0]; -// CompoundClass compound = c.GetCompoundClass() as CompoundClass; -// -// IMethod invokeMethod = completionItem.EventType.ReturnType.GetMethods().FirstOrDefault(m => m.Name == "Invoke"); -// -// string handlerName = completionItem.HandlerName; -// -// if (invokeMethod == null) -// throw new ArgumentException("delegateType is not a valid delegate!"); -// -// if (compound != null) { -// foreach (IClass part in compound.Parts) { -// IMember lastMember = part.Methods.LastOrDefault(); -// -// if (lastMember != null && lastMember.ToString() == initializeComponent.ToString()) -// continue; -// -// if (completionItem.EventType.ReturnType == null) -// return false; -// -// while (part.Methods.Any(m => m.Name == handlerName && -// m.Parameters.Count == invokeMethod.Parameters.Count && -// m.Parameters.SequenceEqual(invokeMethod.Parameters, new ParameterComparer()) -// )) { -// handlerName = completionItem.HandlerName + discriminator; -// discriminator++; -// } -// -// discriminator--; -// -// ParametrizedNode node = (ParametrizedNode)CodeGenerator.ConvertMember(invokeMethod, new ClassFinder(part, context.Editor.Caret.Line, context.Editor.Caret.Column)); -// -// node.Name = handlerName; -// -// node.Modifier = Modifiers.None; -// -// IViewContent viewContent = FileService.OpenFile(part.SyntaxTree.FileName, XamlBindingOptions.SwitchToCodeViewAfterInsertion); -// IFileDocumentProvider document = viewContent as IFileDocumentProvider; -// -// if (viewContent != null && document != null) { -// DomRegion domRegion; -// -// if (lastMember != null) { -// unit.ProjectContent.Language.CodeGenerator.InsertCodeAfter(lastMember, new RefactoringDocumentAdapter(document.GetDocumentForFile(viewContent.PrimaryFile)), node); -// domRegion = lastMember.BodyRegion; -// } -// else { -// unit.ProjectContent.Language.CodeGenerator.InsertCodeAtEnd(part.Region, new RefactoringDocumentAdapter(document.GetDocumentForFile(viewContent.PrimaryFile)), node); -// domRegion = part.Region; -// } -// -// // move caret to generated code -// ITextEditorProvider provider = viewContent as ITextEditorProvider; -// if (provider != null) { -// provider.TextEditor.JumpTo(domRegion.EndLine + 2, domRegion.EndColumn - 1); -// } -// } -// return true; -// } -// } -// -// return false; -// } + public override void Complete(CompletionContext context, ICompletionItem item) + { + using (context.Editor.Document.OpenUndoGroup()) { + base.Complete(context, item); + + XamlCompletionContext xamlContext = XamlContextResolver.ResolveCompletionContext(context.Editor, context.CompletionChar); + + if (xamlContext.Description == XamlContextDescription.None && (context.StartOffset <= 0 || context.Editor.Document.GetCharAt(context.StartOffset - 1) != '<')) { + context.Editor.Document.Insert(context.StartOffset, "<"); + context.EndOffset++; + } + + if (item is XamlCompletionItem) { + XamlCompletionItem cItem = item as XamlCompletionItem; + + if (cItem.Entity is IProperty || cItem.Entity is IEvent) { + if (xamlContext.Description == XamlContextDescription.InTag) { + context.Editor.Document.Insert(context.EndOffset, "=\"\""); + context.CompletionCharHandled = context.CompletionChar == '='; + context.Editor.Caret.Offset--; + new XamlCodeCompletionBinding().CtrlSpace(context.Editor); + } else if (xamlContext.Description == XamlContextDescription.InMarkupExtension && !string.IsNullOrEmpty(xamlContext.RawAttributeValue)) { + string valuePart = xamlContext.RawAttributeValue.Substring(0, xamlContext.ValueStartOffset); + AttributeValue value = MarkupExtensionParser.ParseValue(valuePart); + + if (value != null && !value.IsString) { + var markup = Utils.GetMarkupExtensionAtPosition(value.ExtensionValue, context.Editor.Caret.Offset); + if (markup.NamedArguments.Count > 0 || markup.PositionalArguments.Count > 0) { + int oldOffset = context.Editor.Caret.Offset; + context.Editor.Caret.Offset = context.StartOffset; + string word = context.Editor.GetWordBeforeCaret().TrimEnd(); + int spaces = CountWhiteSpacesAtEnd(context.Editor.GetWordBeforeCaret()); + int typeNameStart = markup.ExtensionType.IndexOf(':') + 1; + + if (!(word == "." || word == "," || word == ":") && markup.ExtensionType.Substring(typeNameStart, markup.ExtensionType.Length - typeNameStart) != word) { + context.Editor.Document.Replace(context.Editor.Caret.Offset - spaces, spaces, ", "); + oldOffset += (2 - spaces); + } + + context.Editor.Caret.Offset = oldOffset; + } + } + + if (cItem.Text.EndsWith("=", StringComparison.OrdinalIgnoreCase)) + new XamlCodeCompletionBinding().CtrlSpace(context.Editor); + } + } + } + + if (item is NewEventCompletionItem) { + CreateEventHandlerCode(xamlContext, item as NewEventCompletionItem); + } + + if (item is XmlnsCompletionItem) { + context.Editor.Caret.Offset++; + } + + switch (item.Text) { + case "![CDATA[": + context.Editor.Document.Insert(context.Editor.Caret.Offset, "]]>"); + context.Editor.Caret.Offset -= 3; + break; + case "?": + context.Editor.Document.Insert(context.Editor.Caret.Offset, "?>"); + context.Editor.Caret.Offset -= 2; + break; + case "!--": + context.Editor.Document.Insert(context.Editor.Caret.Offset, " -->"); + context.Editor.Caret.Offset -= 4; + break; + } + + if (item.Text.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { + context.Editor.Document.Insert(context.EndOffset, ">"); + context.CompletionCharHandled = context.CompletionChar == '>'; + context.Editor.Caret.Offset++; + } + } + } + + static bool CreateEventHandlerCode(XamlCompletionContext context, NewEventCompletionItem completionItem) + { + IProject project = SD.ProjectService.FindProjectContainingFile(context.Editor.FileName); + if (project == null) return false; + var unresolved = context.ParseInformation.UnresolvedFile.TypeDefinition; + if (unresolved == null) return false; + var compilation = SD.ParserService.GetCompilationForFile(context.Editor.FileName); + var definition = unresolved.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)).GetDefinition(); + project.CodeGenerator.InsertEventHandler(definition, completionItem.HandlerName, completionItem.EventType, true); + return true; + } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs index 97f3ea5481..394aa2d767 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs @@ -80,18 +80,18 @@ namespace ICSharpCode.XamlBinding IDocument document = null; foreach (var tag in TreeTraversal.PreOrder(xmlDocument, node => node.Children).OfType().Where(t => t.IsComment)) { int matchLength; - AXmlText comment = tag.Children.OfType().First(); - int index = comment.Value.IndexOfAny(TaskListTokens, 0, out matchLength); + string commentText = fileContent.GetText(tag.StartOffset, tag.Length); + int index = commentText.IndexOfAny(TaskListTokens, 0, out matchLength); if (index > -1) { if (document == null) document = fileContent as IDocument ?? new ReadOnlyDocument(fileContent, parseInfo.FileName); do { - TextLocation startLocation = document.GetLocation(comment.StartOffset + index); - int startOffset = index + comment.StartOffset; - int endOffset = Math.Min(document.GetLineByOffset(startOffset).EndOffset, comment.EndOffset); + TextLocation startLocation = document.GetLocation(tag.StartOffset + index); + int startOffset = index + tag.StartOffset; + int endOffset = Math.Min(document.GetLineByOffset(startOffset).EndOffset, tag.EndOffset); string content = document.GetText(startOffset, endOffset - startOffset); parseInfo.TagComments.Add(new TagComment(content.Substring(0, matchLength), new DomRegion(parseInfo.FileName, startLocation.Line, startLocation.Column), content.Substring(matchLength))); - index = comment.Value.IndexOfAny(TaskListTokens, endOffset - comment.StartOffset, out matchLength); + index = commentText.IndexOfAny(TaskListTokens, endOffset - tag.StartOffset, out matchLength); } while (index > -1); } }